aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/GattCharacteristic.h8
-rw-r--r--src/GattUuid.h2
-rw-r--r--src/HciAdapter.cpp671
-rw-r--r--src/HciAdapter.h368
-rw-r--r--src/HciSocket.cpp70
-rw-r--r--src/HciSocket.h3
-rw-r--r--src/Init.cpp147
-rw-r--r--src/Mgmt.cpp172
-rw-r--r--src/Mgmt.h180
-rw-r--r--src/TickEvent.h2
10 files changed, 995 insertions, 628 deletions
diff --git a/src/GattCharacteristic.h b/src/GattCharacteristic.h
index 3815b2a..7284631 100644
--- a/src/GattCharacteristic.h
+++ b/src/GattCharacteristic.h
@@ -40,6 +40,7 @@
#include "Utils.h"
#include "TickEvent.h"
#include "GattInterface.h"
+#include "HciAdapter.h"
namespace ggk {
@@ -207,9 +208,16 @@ struct GattCharacteristic : GattInterface
//
// This is a helper method that accepts common types. For custom types, there is a form that accepts a `GVariant *`, called
// `sendChangeNotificationVariant()`.
+ //
+ // If there are no connections, this method returns doing nothing.
template<typename T>
void sendChangeNotificationValue(GDBusConnection *pBusConnection, T value) const
{
+ if (HciAdapter::getInstance().getActiveConnectionCount() == 0)
+ {
+ return;
+ }
+
GVariant *pVariant = Utils::gvariantFromByteArray(value);
sendChangeNotificationVariant(pBusConnection, pVariant);
}
diff --git a/src/GattUuid.h b/src/GattUuid.h
index 9d95754..6cfb9ca 100644
--- a/src/GattUuid.h
+++ b/src/GattUuid.h
@@ -159,7 +159,7 @@ struct GattUuid
{
bitCount = 32;
char partStr[9];
- snprintf(partStr, sizeof(partStr), "%04x", part);
+ snprintf(partStr, sizeof(partStr), "%08x", part);
uuid = std::string(partStr) + kGattStandardUuidSuffix;
}
diff --git a/src/HciAdapter.cpp b/src/HciAdapter.cpp
index 9000709..06c4457 100644
--- a/src/HciAdapter.cpp
+++ b/src/HciAdapter.cpp
@@ -57,130 +57,391 @@
#include "HciAdapter.h"
#include "HciSocket.h"
#include "Utils.h"
+#include "Mgmt.h"
#include "Logger.h"
namespace ggk {
-static const int kMinCommandCode = 0x0001;
-static const int kMaxCommandCode = 0x0043;
-static const char *kCommandCodeNames[kMaxCommandCode] =
+// Our event thread listens for events coming from the adapter and deals with them appropriately
+std::thread HciAdapter::eventThread;
+
+const char * const HciAdapter::kCommandCodeNames[kMaxCommandCode + 1] =
{
- "Read Management Version Information Command", // 0x0001
- "Read Management Supported Commands Command", // 0x0002
- "Read Controller Index List Command", // 0x0003
- "Read Controller Information Command", // 0x0004
- "Set Powered Command", // 0x0005
- "Set Discoverable Command", // 0x0006
- "Set Connectable Command", // 0x0007
- "Set Fast Connectable Command", // 0x0008
- "Set Bondable Command", // 0x0009
- "Set Link Security Command", // 0x000A
- "Set Secure Simple Pairing Command", // 0x000B
- "Set High Speed Command", // 0x000C
- "Set Low Energy Command", // 0x000D
- "Set Device Class", // 0x000E
- "Set Local Name Command", // 0x000F
- "Add UUID Command", // 0x0010
- "Remove UUID Command", // 0x0011
- "Load Link Keys Command", // 0x0012
- "Load Long Term Keys Command", // 0x0013
- "Disconnect Command", // 0x0014
- "Get Connections Command", // 0x0015
- "PIN Code Reply Command", // 0x0016
- "PIN Code Negative Reply Command", // 0x0017
- "Set IO Capability Command", // 0x0018
- "Pair Device Command", // 0x0019
- "Cancel Pair Device Command", // 0x001A
- "Unpair Device Command", // 0x001B
- "User Confirmation Reply Command", // 0x001C
- "User Confirmation Negative Reply Command", // 0x001D
- "User Passkey Reply Command", // 0x001E
- "User Passkey Negative Reply Command", // 0x001F
- "Read Local Out Of Band Data Command", // 0x0020
- "Add Remote Out Of Band Data Command", // 0x0021
- "Remove Remote Out Of Band Data Command", // 0x0022
- "Start Discovery Command", // 0x0023
- "Stop Discovery Command", // 0x0024
- "Confirm Name Command", // 0x0025
- "Block Device Command", // 0x0026
- "Unblock Device Command", // 0x0027
- "Set Device ID Command", // 0x0028
- "Set Advertising Command", // 0x0029
- "Set BR/EDR Command", // 0x002A
- "Set Static Address Command", // 0x002B
- "Set Scan Parameters Command", // 0x002C
- "Set Secure Connections Command", // 0x002D
- "Set Debug Keys Command", // 0x002E
- "Set Privacy Command", // 0x002F
- "Load Identity Resolving Keys Command", // 0x0030
- "Get Connection Information Command", // 0x0031
- "Get Clock Information Command", // 0x0032
- "Add Device Command", // 0x0033
- "Remove Device Command", // 0x0034
- "Load Connection Parameters Command", // 0x0035
- "Read Unconfigured Controller Index List Command", // 0x0036
+ "Invalid Command", // 0x0000
+ "Read Version Information Command", // 0x0001
+ "Read Supported Commands Command", // 0x0002
+ "Read Controller Index List Command", // 0x0003
+ "Read Controller Information Command", // 0x0004
+ "Set Powered Command", // 0x0005
+ "Set Discoverable Command", // 0x0006
+ "Set Connectable Command", // 0x0007
+ "Set Fast Connectable Command", // 0x0008
+ "Set Bondable Command", // 0x0009
+ "Set Link Security Command", // 0x000A
+ "Set Secure Simple Pairing Command", // 0x000B
+ "Set High Speed Command", // 0x000C
+ "Set Low Energy Command", // 0x000D
+ "Set Device Class", // 0x000E
+ "Set Local Name Command", // 0x000F
+ "Add UUID Command", // 0x0010
+ "Remove UUID Command", // 0x0011
+ "Load Link Keys Command", // 0x0012
+ "Load Long Term Keys Command", // 0x0013
+ "Disconnect Command", // 0x0014
+ "Get Connections Command", // 0x0015
+ "PIN Code Reply Command", // 0x0016
+ "PIN Code Negative Reply Command", // 0x0017
+ "Set IO Capability Command", // 0x0018
+ "Pair Device Command", // 0x0019
+ "Cancel Pair Device Command", // 0x001A
+ "Unpair Device Command", // 0x001B
+ "User Confirmation Reply Command", // 0x001C
+ "User Confirmation Negative Reply Command", // 0x001D
+ "User Passkey Reply Command", // 0x001E
+ "User Passkey Negative Reply Command", // 0x001F
+ "Read Local Out Of Band Data Command", // 0x0020
+ "Add Remote Out Of Band Data Command", // 0x0021
+ "Remove Remote Out Of Band Data Command", // 0x0022
+ "Start Discovery Command", // 0x0023
+ "Stop Discovery Command", // 0x0024
+ "Confirm Name Command", // 0x0025
+ "Block Device Command", // 0x0026
+ "Unblock Device Command", // 0x0027
+ "Set Device ID Command", // 0x0028
+ "Set Advertising Command", // 0x0029
+ "Set BR/EDR Command", // 0x002A
+ "Set Static Address Command", // 0x002B
+ "Set Scan Parameters Command", // 0x002C
+ "Set Secure Connections Command", // 0x002D
+ "Set Debug Keys Command", // 0x002E
+ "Set Privacy Command", // 0x002F
+ "Load Identity Resolving Keys Command", // 0x0030
+ "Get Connection Information Command", // 0x0031
+ "Get Clock Information Command", // 0x0032
+ "Add Device Command", // 0x0033
+ "Remove Device Command", // 0x0034
+ "Load Connection Parameters Command", // 0x0035
+ "Read Unconfigured Controller Index List Command", // 0x0036
"Read Controller Configuration Information Command", // 0x0037
- "Set External Configuration Command", // 0x0038
- "Set Public Address Command", // 0x0039
- "Start Service Discovery Command", // 0x003a
- "Read Local Out Of Band Extended Data Command", // 0x003b
- "Read Extended Controller Index List Command", // 0x003c
- "Read Advertising Features Command", // 0x003d
- "Add Advertising Command", // 0x003e
- "Remove Advertising Command", // 0x003f
- "Get Advertising Size Information Command", // 0x0040
- "Start Limited Discovery Command", // 0x0041
- "Read Extended Controller Information Command", // 0x0042
+ "Set External Configuration Command", // 0x0038
+ "Set Public Address Command", // 0x0039
+ "Start Service Discovery Command", // 0x003a
+ "Read Local Out Of Band Extended Data Command", // 0x003b
+ "Read Extended Controller Index List Command", // 0x003c
+ "Read Advertising Features Command", // 0x003d
+ "Add Advertising Command", // 0x003e
+ "Remove Advertising Command", // 0x003f
+ "Get Advertising Size Information Command", // 0x0040
+ "Start Limited Discovery Command", // 0x0041
+ "Read Extended Controller Information Command", // 0x0042
// NOTE: The documentation at https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/mgmt-api.txt) states that the command
// code for "Set Appearance Command" is 0x0042. It also says this about the previous command in the list ("Read Extended
// Controller Information Command".) This is likely an error, so I'm following the order of the commands as they appear in the
// documentation. This makes "Set Appearance Code" have a command code of 0x0043.
- "Set Appearance Command", // 0x0043
+ "Set Appearance Command" // 0x0043
+};
+
+const char * const HciAdapter::kEventTypeNames[kMaxEventType + 1] =
+{
+ "Invalid Event", // 0x0000
+ "Command Complete Event", // 0x0001
+ "Command Status Event", // 0x0002
+ "Controller Error Event", // 0x0003
+ "Index Added Event", // 0x0004
+ "Index Removed Event", // 0x0005
+ "New Settings Event", // 0x0006
+ "Class Of Device Changed Event", // 0x0007
+ "Local Name Changed Event", // 0x0008
+ "New Link Key Event", // 0x0009
+ "New Long Term Key Event", // 0x000A
+ "Device Connected Event", // 0x000B
+ "Device Disconnected Event", // 0x000C
+ "Connect Failed Event", // 0x000D
+ "PIN Code Request Event", // 0x000E
+ "User Confirmation Request Event", // 0x000F
+ "User Passkey Request Event", // 0x0010
+ "Authentication Failed Event", // 0x0011
+ "Device Found Event", // 0x0012
+ "Discovering Event", // 0x0013
+ "Device Blocked Event", // 0x0014
+ "Device Unblocked Event", // 0x0015
+ "Device Unpaired Event", // 0x0016
+ "Passkey Notify Event", // 0x0017
+ "New Identity Resolving Key Event", // 0x0018
+ "New Signature Resolving Key Event", // 0x0019
+ "Device Added Event", // 0x001a
+ "Device Removed Event", // 0x001b
+ "New Connection Parameter Event", // 0x001c
+ "Unconfigured Index Added Event", // 0x001d
+ "Unconfigured Index Removed Event", // 0x001e
+ "New Configuration Options Event", // 0x001f
+ "Extended Index Added Event", // 0x0020
+ "Extended Index Removed Event", // 0x0021
+ "Local Out Of Band Extended Data Updated Event", // 0x0022
+ "Advertising Added Event", // 0x0023
+ "Advertising Removed Event", // 0x0024
+ "Extended Controller Information Changed Event" // 0x0025
};
-static const int kMinEventType = 0x0001;
-static const int kMaxEventType = 0x0025;
-static const char *kEventTypeNames[kMaxEventType] =
+const char * const HciAdapter::kStatusCodes[kMaxStatusCode + 1] =
{
- "Command Complete Event", // 0x0001
- "Command Status Event", // 0x0002
- "Controller Error Event", // 0x0003
- "Index Added Event", // 0x0004
- "Index Removed Event", // 0x0005
- "New Settings Event", // 0x0006
- "Class Of Device Changed Event", // 0x0007
- "Local Name Changed Event", // 0x0008
- "New Link Key Event", // 0x0009
- "New Long Term Key Event", // 0x000A
- "Device Connected Event", // 0x000B
- "Device Disconnected Event", // 0x000C
- "Connect Failed Event", // 0x000D
- "PIN Code Request Event", // 0x000E
- "User Confirmation Request Event", // 0x000F
- "User Passkey Request Event", // 0x0010
- "Authentication Failed Event", // 0x0011
- "Device Found Event", // 0x0012
- "Discovering Event", // 0x0013
- "Device Blocked Event", // 0x0014
- "Device Unblocked Event", // 0x0015
- "Device Unpaired Event", // 0x0016
- "Passkey Notify Event", // 0x0017
- "New Identity Resolving Key Event", // 0x0018
- "New Signature Resolving Key Event", // 0x0019
- "Device Added Event", // 0x001a
- "Device Removed Event", // 0x001b
- "New Connection Parameter Event", // 0x001c
- "Unconfigured Index Added Event", // 0x001d
- "Unconfigured Index Removed Event", // 0x001e
- "New Configuration Options Event", // 0x001f
- "Extended Index Added Event", // 0x0020
- "Extended Index Removed Event", // 0x0021
- "Local Out Of Band Extended Data Updated Event", // 0x0022
- "Advertising Added Event", // 0x0023
- "Advertising Removed Event", // 0x0024
- "Extended Controller Information Changed Event" // 0x0025
+ "Success", // 0x00
+ "Unknown Command", // 0x01
+ "Not Connected", // 0x02
+ "Failed", // 0x03
+ "Connect Failed", // 0x04
+ "Authentication Failed", // 0x05
+ "Not Paired", // 0x06
+ "No Resources", // 0x07
+ "Timeout", // 0x08
+ "Already Connected", // 0x09
+ "Busy", // 0x0A
+ "Rejected", // 0x0B
+ "Not Supported", // 0x0C
+ "Invalid Parameters", // 0x0D
+ "Disconnected", // 0x0E
+ "Not Powered", // 0x0F
+ "Cancelled", // 0x10
+ "Invalid Index", // 0x11
+ "RFKilled", // 0x12
+ "Already Paired", // 0x13
+ "Permission Denied", // 0x14
};
+HciAdapter::AdapterSettings HciAdapter::getAdapterSettings()
+{
+ return adapterSettings;
+}
+
+HciAdapter::ControllerInformation HciAdapter::getControllerInformation()
+{
+ return controllerInformation;
+}
+
+HciAdapter::VersionInformation HciAdapter::getVersionInformation()
+{
+ return versionInformation;
+}
+
+HciAdapter::LocalName HciAdapter::getLocalName()
+{
+ return localName;
+}
+
+// Our thread interface, which simply launches our the thread processor on our HciAdapter instance
+void runEventThread()
+{
+ HciAdapter::getInstance().runEventThread();
+}
+
+// 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 HciAdapter::runEventThread()
+{
+ Logger::trace("Entering the HciAdapter event thread");
+
+ while (ggkGetServerRunState() <= ERunning)
+ {
+ // Read the next event, waiting until one arrives
+ std::vector<uint8_t> responsePacket = std::vector<uint8_t>();
+ if (!hciSocket.read(responsePacket))
+ {
+ break;
+ }
+
+ // Do we have enough to check the event code?
+ if (responsePacket.size() < 2)
+ {
+ Logger::error(SSTR << "Invalid command response: too short");
+ continue;
+ }
+
+ // Our response, as a usable object type
+ uint16_t eventCode = Utils::endianToHost(*reinterpret_cast<uint16_t *>(responsePacket.data()));
+
+ // Ensure our event code is valid
+ if (eventCode < HciAdapter::kMinEventType || eventCode > HciAdapter::kMaxEventType)
+ {
+ Logger::error(SSTR << "Invalid command response: event code (" << eventCode << ") out of range");
+ continue;
+ }
+
+ switch(eventCode)
+ {
+ // Command complete event
+ case Mgmt::ECommandCompleteEvent:
+ {
+ // Extract our event
+ CommandCompleteEvent event = *reinterpret_cast<CommandCompleteEvent *>(responsePacket.data());
+
+ // Fixup endian
+ event.toHost();
+
+ // Log it
+ Logger::debug(event.debugText());
+
+ // Point to the data following the event
+ uint8_t *data = responsePacket.data() + sizeof(CommandCompleteEvent);
+ size_t dataLen = responsePacket.size() - sizeof(CommandCompleteEvent);
+
+ switch(event.commandCode)
+ {
+ // We just log the version/revision info
+ case Mgmt::EReadVersionInformationCommand:
+ {
+ // Verify the size is what we expect
+ if (dataLen != sizeof(VersionInformation))
+ {
+ Logger::error("Invalid data length");
+ return;
+ }
+
+ versionInformation = *reinterpret_cast<VersionInformation *>(data);
+ versionInformation.toHost();
+ Logger::debug(versionInformation.debugText());
+ break;
+ }
+ case Mgmt::EReadControllerInformationCommand:
+ {
+ if (dataLen != sizeof(ControllerInformation))
+ {
+ Logger::error("Invalid data length");
+ return;
+ }
+
+ controllerInformation = *reinterpret_cast<ControllerInformation *>(data);
+ controllerInformation.toHost();
+ Logger::debug(controllerInformation.debugText());
+ break;
+ }
+ case Mgmt::ESetLocalNameCommand:
+ {
+ if (dataLen != sizeof(LocalName))
+ {
+ Logger::error("Invalid data length");
+ return;
+ }
+
+ localName = *reinterpret_cast<LocalName *>(data);
+ Logger::info(localName.debugText());
+ break;
+ }
+ case Mgmt::ESetPoweredCommand:
+ case Mgmt::ESetBREDRCommand:
+ case Mgmt::ESetSecureConnectionsCommand:
+ case Mgmt::ESetBondableCommand:
+ case Mgmt::ESetConnectableCommand:
+ case Mgmt::ESetLowEnergyCommand:
+ case Mgmt::ESetAdvertisingCommand:
+ {
+ if (dataLen != sizeof(AdapterSettings))
+ {
+ Logger::error("Invalid data length");
+ return;
+ }
+
+ adapterSettings = *reinterpret_cast<AdapterSettings *>(data);
+ adapterSettings.toHost();
+
+ Logger::debug(adapterSettings.debugText());
+ break;
+ }
+ }
+ break;
+ }
+ // Command status event
+ case Mgmt::ECommandStatusEvent:
+ {
+ // Extract our event
+ CommandStatusEvent event = *reinterpret_cast<CommandStatusEvent *>(responsePacket.data());
+
+ // Fixup endian
+ event.toHost();
+
+ // Log it
+ Logger::debug(event.debugText());
+
+ break;
+ }
+ // Command status event
+ case Mgmt::EDeviceConnectedEvent:
+ {
+ // Extract our event
+ DeviceConnectedEvent event = *reinterpret_cast<DeviceConnectedEvent *>(responsePacket.data());
+
+ // Fixup endian
+ event.toHost();
+
+ // Log it
+ Logger::debug(event.debugText());
+
+ // Track our connection count
+ activeConnections += 1;
+
+ break;
+ }
+ // Command status event
+ case Mgmt::EDeviceDisconnectedEvent:
+ {
+ // Extract our event
+ DeviceDisconnectedEvent event = *reinterpret_cast<DeviceDisconnectedEvent *>(responsePacket.data());
+
+ // Fixup endian
+ event.toHost();
+
+ // Log it
+ Logger::debug(event.debugText());
+
+ // Track our connection count
+ activeConnections -= 1;
+
+ break;
+ }
+ // Unsupported
+ default:
+ {
+ if (eventCode >= kMinEventType && eventCode <= kMaxEventType)
+ {
+ Logger::error("Unsupported response event type: " + Utils::hex(eventCode) + " (" + kEventTypeNames[eventCode] + ")");
+ }
+ else
+ {
+ Logger::error("Invalid event type response: " + Utils::hex(eventCode));
+ }
+ }
+ }
+ }
+
+ Logger::trace("Leaving the HciAdapter event thread");
+}
+
+// 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 HciAdapter::sync(uint16_t controllerIndex)
+{
+ HciAdapter::HciHeader request;
+ request.code = Mgmt::EReadVersionInformationCommand;
+ request.controllerId = HciAdapter::kNonController;
+ request.dataSize = 0;
+
+ if (!HciAdapter::getInstance().sendCommand(request))
+ {
+ Logger::error("Failed to get version information");
+ }
+
+ request.code = Mgmt::EReadControllerInformationCommand;
+ request.controllerId = controllerIndex;
+ request.dataSize = 0;
+
+ if (!HciAdapter::getInstance().sendCommand(request))
+ {
+ Logger::error("Failed to get current settings");
+ }
+}
+
// Connects the HCI socket if a connection does not already exist
//
// If a connection already exists, this method will do nothing and return true.
@@ -190,13 +451,36 @@ static const char *kEventTypeNames[kMaxEventType] =
// Returns true if the HCI socket is connected (either via a new connection or an existing one), otherwise false
bool HciAdapter::connect()
{
- // Connect if we aren't already connected
- if (!isConnected() && !hciSocket.connect())
+ // Already connected?
+ if (isConnected())
+ {
+ return true;
+ }
+
+ // Try to connect
+ if (!hciSocket.connect())
{
disconnect();
return false;
}
+ Logger::trace("Starting the HciAdapter thread");
+
+ // Create a thread to read the data from the socket
+ try
+ {
+ eventThread = std::thread(ggk::runEventThread);
+ }
+ catch(std::system_error &ex)
+ {
+ if (ex.code() == std::errc::resource_unavailable_try_again)
+ {
+ Logger::error(SSTR << "HciAdapter thread was unable to start: " << ex.what());
+ disconnect();
+ return false;
+ }
+ }
+
return true;
}
@@ -219,6 +503,14 @@ void HciAdapter::disconnect()
{
hciSocket.disconnect();
}
+
+ if (eventThread.joinable())
+ {
+ Logger::trace("Stopping the HciAdapter thread");
+
+ pthread_kill(eventThread.native_handle(), SIGINT);
+ eventThread.join();
+ }
}
// Sends a command over the HCI socket
@@ -227,15 +519,12 @@ void HciAdapter::disconnect()
// a failure is returned.
//
// Returns true on success, otherwise false
-bool HciAdapter::sendCommand(Header &request, ResponseEvent &response, int responseLen)
+bool HciAdapter::sendCommand(HciHeader &request)
{
// Auto-connect
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");
+ Logger::debug(request.debugText());
request.toNetwork();
uint8_t *pRequest = reinterpret_cast<uint8_t *>(&request);
@@ -245,156 +534,8 @@ bool HciAdapter::sendCommand(Header &request, ResponseEvent &response, int respo
return false;
}
- // Read the response for this particular command
- return readResponse(request.code, response, responseLen);
-}
-
-// Reads a response 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 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;
- }
-
- // Validate our events and remove everything that isn't a command complete or status event
- if (!filterAndValidateEvents(commandCode, responsePacket))
- {
- return false;
- }
-
- // Do we have enough to check the event code?
- if (responsePacket.size() < 2)
- {
- Logger::error(SSTR << "Command's response was invalid");
- return false;
- }
-
- // If this wasn't a Command Complete Event, just return an error
- uint16_t eventCode = Utils::endianToHost(*reinterpret_cast<uint16_t *>(responsePacket.data()));
- if (eventCode != 1)
- {
- return false;
- }
-
- // It's a Command Complete event, verify the size is what we expect
- if (responsePacket.size() != responseLen)
- {
- Logger::error(SSTR << "Command's response was " << responsePacket.size() << " bytes but was supposed to be " << responseLen << " bytes");
- return false;
- }
-
- // Copy the response data into the requested structure
- memcpy(&response, responsePacket.data(), responseLen);
-
- // Perform endian conversion
- response.toHost();
-
- // Log the header information
- 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));
-
- // One last check... let's verify that our data size is what it should be
- //
- // Note that we add 3 - this is because there are two fields that always follow the
- // data size (so they were included in our ResponseEvent structure) even
- // though they are not counted as part of the size, so we account for that here.
- 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");
- }
-
- // Everything checks out - good to go!
- return true;
-}
-
-// 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 HciAdapter::filterAndValidateEvents(uint16_t commandCode, std::vector<uint8_t> &buffer) const
-{
- // Chew through each event in the buffer, removing those that are not related to the requested commandCode
- std::vector<uint8_t> lastGoodEvent;
- while (!buffer.empty())
- {
- // Get an event pointer into the start of our buffer
- ResponseEvent *pEvent = reinterpret_cast<ResponseEvent *>(buffer.data());
-
- // Validate that there is enough space for this event according to its size
- size_t dataLength = sizeof(Header) + pEvent->header.dataSize;
- if (dataLength > buffer.size())
- {
- Logger::error(" + Not enough data for the current event");
- return false;
- }
-
- // !HACK! - If the device is connected, then powering off (Command Code 0x0005) returns a Device Disconnect Event (Event
- // Code 0xC) rather than a Command Complete Event (0x1) like it should.
- //
- // We'll fake it here, converting that condition into a Command Complete Event (0x1).
- if (commandCode == 0x0005 && pEvent->header.code == 0xC)
- {
- Logger::debug("!HACK! Converting invalid Disconect Event to Command Complete Event for power-off command");
- pEvent->header.code = 1;
- buffer = {0x1, 0x0, 0x0, 0x0, 0x7, 0x0, 0x5, 0x0, 0x0, 0x2, 0x6, 0x0, 0x0};
- pEvent = reinterpret_cast<ResponseEvent *>(buffer.data());
- dataLength = sizeof(Header) + pEvent->header.dataSize;
- }
-
- // Check the event type
- 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 << ")");
-
- // Success event for our command?
- 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);
- }
-
- // Remove the current event and move along
- buffer.erase(buffer.begin(), buffer.begin() + dataLength);
- }
-
- // If we don't have a last good event, return an error
- if (lastGoodEvent.empty())
- {
- return false;
- }
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
- // Return the last good event
- buffer = lastGoodEvent;
return true;
}
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
diff --git a/src/HciSocket.cpp b/src/HciSocket.cpp
index c3bcc3e..214ef85 100644
--- a/src/HciSocket.cpp
+++ b/src/HciSocket.cpp
@@ -43,6 +43,7 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <thread>
+#include <fcntl.h>
#include "HciSocket.h"
#include "Logger.h"
@@ -114,55 +115,50 @@ void HciSocket::disconnect()
// Reads data from the HCI socket
//
-// Raw data is read until no more data is available. If no data is available when this method initially starts to read, it will
-// retry for a maximum timeout defined by `kMaxRetryTimeMS`.
+// Raw data is read and returned in `response`.
//
-// Returns true if any data was read successfully, otherwise false is returned in the case of an error or a timeout.
+// Returns true if data was read successfully, otherwise false is returned. A false return code does not necessarily depict
+// an error, as this can arise from expected conditions (such as an interrupt.)
bool HciSocket::read(std::vector<uint8_t> &response) const
{
- // Clear out our response
- response.clear();
+ // Fill our response with empty data
+ response.resize(kResponseMaxSize, 0);
- uint8_t responseChunk[kResponseChunkSize];
+ // Ensure we have blocking I/O
+ fcntl(fdSocket, F_SETFL, 0);
- int retryTimeMS = 0;
- while (retryTimeMS < kMaxRetryTimeMS && !ggkIsServerRunning())
+ // Block until we receive data, a disconnect, or a signal
+ ssize_t bytesRead = ::recv(fdSocket, &response[0], kResponseMaxSize, MSG_WAITALL);
+
+ // If there was an error, wipe the data and return an error condition
+ if (bytesRead < 0)
{
- ssize_t bytesRead = ::read(fdSocket, responseChunk, kResponseChunkSize);
- if (bytesRead > 0)
+ if (errno == EINTR)
{
- if (response.size() + bytesRead > kResponseMaxSize)
- {
- Logger::warn(SSTR << "Response has exceeded maximum size");
- return false;
- }
- // We just received some data, add it to our buffer
- std::vector<uint8_t> insertBuf(responseChunk, responseChunk + bytesRead);
- response.insert(response.end(), insertBuf.begin(), insertBuf.end());
+ Logger::debug("HciSocket receive interrupted");
}
else
{
- // If we have data, we're at the end
- if (response.size() != 0)
- {
- break;
- }
+ logErrno("recv");
}
-
- // Retry (or continue reading)
- std::this_thread::sleep_for(std::chrono::milliseconds(kRetryIntervalMS));
- retryTimeMS += kRetryIntervalMS;
+ response.resize(0);
+ return false;
}
-
- // Did we time out?
- if (retryTimeMS >= kMaxRetryTimeMS)
+ else if (bytesRead == 0)
{
- logErrno("read(header)");
+ Logger::error(" + Peer closed the socket");
+ response.resize(0);
return false;
}
- Logger::debug(SSTR << " + Read " << response.size() << " bytes");
- Logger::debug(SSTR << Utils::hex(response.data(), response.size()));
+ // We have data
+ response.resize(bytesRead);
+
+ std::string dump = "";
+ dump += "> Read " + std::to_string(response.size()) + " bytes\n";
+ dump += Utils::hex(response.data(), response.size());
+ Logger::debug(dump);
+
return true;
}
@@ -179,8 +175,10 @@ bool HciSocket::write(std::vector<uint8_t> buffer) const
// This method returns true if the bytes were written successfully, otherwise false
bool HciSocket::write(const uint8_t *pBuffer, size_t count) const
{
- Logger::debug(SSTR << " + Writing " << count << " bytes");
- Logger::debug(SSTR << Utils::hex(pBuffer, count));
+ std::string dump = "";
+ dump += "> Writing " + std::to_string(count) + " bytes\n";
+ dump += Utils::hex(pBuffer, count);
+ Logger::debug(dump);
size_t len = ::write(fdSocket, pBuffer, count);
@@ -199,4 +197,4 @@ void HciSocket::logErrno(const char *pOperation) const
Logger::error(SSTR << "" << pOperation << " on Bluetooth management socket error (" << errno << "): " << strerror(errno));
}
-}; // namespace ggk \ No newline at end of file
+}; // namespace ggk
diff --git a/src/HciSocket.h b/src/HciSocket.h
index d968dc5..20dbe4e 100644
--- a/src/HciSocket.h
+++ b/src/HciSocket.h
@@ -86,9 +86,6 @@ private:
int fdSocket;
const size_t kResponseMaxSize = 64 * 1024;
- const int kResponseChunkSize = 1024;
- const int kRetryIntervalMS = 20;
- const int kMaxRetryTimeMS = 5000;
};
}; // namespace ggk \ No newline at end of file
diff --git a/src/Init.cpp b/src/Init.cpp
index 9be0ee2..49a4866 100644
--- a/src/Init.cpp
+++ b/src/Init.cpp
@@ -298,6 +298,9 @@ void shutdown()
// Our new state: shutting down
setServerRunState(EStopping);
+ // Disconnect our HciAdapter
+ HciAdapter::getInstance().disconnect();
+
// If we still have a main loop, ask it to quit
if (nullptr != pMainLoop)
{
@@ -487,11 +490,17 @@ gboolean onSetProperty
//
// ---------------------------------------------------------------------------------------------------------------------------------
+// Convenience method for setting a retry timer so that operations can be continuously retried until we eventually succeed
+void setRetry()
+{
+ retryTimeStart = time(nullptr);
+}
+
// Convenience method for setting a retry timer so that failures (related to initialization) can be continuously retried until we
// eventually succeed.
void setRetryFailure()
{
- retryTimeStart = time(nullptr);
+ setRetry();
Logger::warn(SSTR << " + Will retry the failed operation in about " << kRetryDelaySeconds << " seconds");
}
@@ -663,127 +672,105 @@ void configureAdapter()
{
Mgmt mgmt;
+ // Get our properly truncated advertising names
+ std::string advertisingName = Mgmt::truncateName(TheServer->getAdvertisingName());
+ std::string advertisingShortName = Mgmt::truncateShortName(TheServer->getAdvertisingShortName());
+
// Find out what our current settings are
- Logger::debug(SSTR << "Getting device information");
- Mgmt::ControllerInformation *pInfo = mgmt.getControllerInformation();
- if (nullptr == pInfo)
+ HciAdapter::ControllerInformation info = HciAdapter::getInstance().getControllerInformation();
+
+ // Are all of our settings the way we want them?
+ bool pwFlag = info.currentSettings.isSet(HciAdapter::EHciPowered) == true;
+ bool leFlag = info.currentSettings.isSet(HciAdapter::EHciLowEnergy) == true;
+ bool brFlag = info.currentSettings.isSet(HciAdapter::EHciBasicRate_EnhancedDataRate) == TheServer->getEnableBREDR();
+ bool scFlag = info.currentSettings.isSet(HciAdapter::EHciSecureConnections) == TheServer->getEnableSecureConnection();
+ bool bnFlag = info.currentSettings.isSet(HciAdapter::EHciBondable) == TheServer->getEnableBondable();
+ bool cnFlag = info.currentSettings.isSet(HciAdapter::EHciConnectable) == TheServer->getEnableConnectable();
+ bool adFlag = info.currentSettings.isSet(HciAdapter::EHciAdvertising) == TheServer->getEnableAdvertising();
+ bool anFlag = (advertisingName.length() == 0 || advertisingName == info.name) && (advertisingShortName.length() == 0 || advertisingShortName == info.shortName);
+
+ // If everything is setup already, we're done
+ if (pwFlag && leFlag && brFlag && scFlag && bnFlag && cnFlag && adFlag && anFlag)
{
- setRetryFailure();
+ Logger::info("The adapter is fully configured");
+
+ // We're all set, nothing to do!
+ bAdapterConfigured = true;
+ initializationStateProcessor();
return;
}
// We need it off to start with
- if ((pInfo->currentSettings & Mgmt::EHciPowered) != 0)
+ if (pwFlag)
{
- Logger::debug(SSTR << "Powering off");
- if (!mgmt.setPowered(false))
- {
- setRetryFailure();
- return;
- }
+ Logger::debug("Powering off");
+ mgmt.setPowered(false);
+ }
+
+ // Enable the LE state (we always set this state if it's not set)
+ if (!leFlag)
+ {
+ Logger::debug("Enabling LE");
+ mgmt.setLE(true);
}
// Change the Br/Edr state?
- bool bredrCurrentState = (pInfo->currentSettings & Mgmt::EHciBasicRate_EnhancedDataRate) != 0 ? true:false;
- if (TheServer->getEnableBREDR() != bredrCurrentState)
+ //
+ // Note that enabling this requries LE to already be enabled or this command will receive a 'rejected' result
+ if (!brFlag)
{
Logger::debug(SSTR << (TheServer->getEnableBREDR() ? "Enabling":"Disabling") << " BR/EDR");
- if (!mgmt.setBredr(TheServer->getEnableBREDR()))
- {
- setRetryFailure();
- return;
- }
+ mgmt.setBredr(TheServer->getEnableBREDR());
}
// Change the Secure Connectinos state?
- bool scCurrentState = (pInfo->currentSettings & Mgmt::EHciSecureConnections) != 0 ? true:false;
- if (TheServer->getEnableSecureConnection() != scCurrentState)
+ if (!scFlag)
{
Logger::debug(SSTR << (TheServer->getEnableSecureConnection() ? "Enabling":"Disabling") << " Secure Connections");
- if (!mgmt.setSecureConnections(TheServer->getEnableSecureConnection() ? 1 : 0))
- {
- setRetryFailure();
- return;
- }
+ mgmt.setSecureConnections(TheServer->getEnableSecureConnection() ? 1 : 0);
}
// Change the Bondable state?
- bool bondableCurrentState = (pInfo->currentSettings & Mgmt::EHciBondable) != 0 ? true:false;
- if (TheServer->getEnableBondable() != bondableCurrentState)
+ if (!bnFlag)
{
Logger::debug(SSTR << (TheServer->getEnableBondable() ? "Enabling":"Disabling") << " Bondable");
- if (!mgmt.setBondable(TheServer->getEnableBondable()))
- {
- setRetryFailure();
- return;
- }
+ mgmt.setBondable(TheServer->getEnableBondable());
}
// Change the Connectable state?
- bool connectableCurrentState = (pInfo->currentSettings & Mgmt::EHciConnectable) != 0 ? true:false;
- if (TheServer->getEnableConnectable() != connectableCurrentState)
+ if (!cnFlag)
{
Logger::debug(SSTR << (TheServer->getEnableConnectable() ? "Enabling":"Disabling") << " Connectable");
- if (!mgmt.setConnectable(TheServer->getEnableConnectable()))
- {
- setRetryFailure();
- return;
- }
- }
-
- // Enable the LE state (we always set this state if it's not set)
- if ((pInfo->currentSettings & Mgmt::EHciLowEnergy) == 0)
- {
- Logger::debug(SSTR << "Enabling LE");
- if (!mgmt.setLE(true))
- {
- setRetryFailure();
- return;
- }
+ mgmt.setConnectable(TheServer->getEnableConnectable());
}
// Change the Advertising state?
- bool advertisingCurrentState = (pInfo->currentSettings & Mgmt::EHciAdvertising) != 0 ? true:false;
- if (TheServer->getEnableAdvertising() != advertisingCurrentState)
+ if (!adFlag)
{
Logger::debug(SSTR << (TheServer->getEnableAdvertising() ? "Enabling":"Disabling") << " Advertising");
- if (!mgmt.setAdvertising(TheServer->getEnableAdvertising() ? 1 : 0))
- {
- setRetryFailure();
- return;
- }
+ mgmt.setAdvertising(TheServer->getEnableAdvertising() ? 1 : 0);
}
// Set the name?
- if (TheServer->getAdvertisingName().length() != 0 || TheServer->getAdvertisingShortName().length() != 0)
+ if (!anFlag)
{
- std::string advertisingName = Mgmt::truncateName(TheServer->getAdvertisingName());
- std::string advertisingShortName = Mgmt::truncateShortName(TheServer->getAdvertisingShortName());
-
- if (advertisingName != pInfo->name || advertisingShortName != pInfo->shortName)
- {
- Logger::info(SSTR << "Setting advertising name to '" << advertisingName << "' (with short name: '" << advertisingShortName << "')");
- if (!mgmt.setName(advertisingName.c_str(), advertisingShortName.c_str()))
- {
- setRetryFailure();
- return;
- }
- }
+ Logger::info(SSTR << "Setting advertising name to '" << advertisingName << "' (with short name: '" << advertisingShortName << "')");
+ mgmt.setName(advertisingName.c_str(), advertisingShortName.c_str());
}
// Turn it back on
- Logger::debug(SSTR << "Powering on");
- if (!mgmt.setPowered(true))
- {
- setRetryFailure();
- return;
- }
+ Logger::debug("Powering on");
+ mgmt.setPowered(true);
+
+ // Give it some extra time to power up - this shouldn't really be necessary, but it won't hurt
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
// We can ignore errors on this - we're just letting it dump the output
- mgmt.getControllerInformation();
+ HciAdapter::getInstance().getControllerInformation();
- bAdapterConfigured = true;
- initializationStateProcessor();
+ // We always set the retry (silently) so we can validate our settings once the adapter has had a chance to respond to all
+ // of our requests
+ setRetry();
}
// ---------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/Mgmt.cpp b/src/Mgmt.cpp
index 34537f0..e61653e 100644
--- a/src/Mgmt.cpp
+++ b/src/Mgmt.cpp
@@ -48,92 +48,7 @@ namespace ggk {
Mgmt::Mgmt(uint16_t controllerIndex)
: controllerIndex(controllerIndex)
{
-}
-
-// Returns the version information:
-//
-// bits 0-15 = revision
-// bits 16-23 = version
-//
-// ... or -1 on error
-int Mgmt::getVersion()
-{
- struct SResponse : HciAdapter::ResponseEvent
- {
- uint8_t version;
- uint16_t revision;
-
- void toHost()
- {
- revision = Utils::endianToHost(revision);
- }
- } __attribute__((packed));
-
- HciAdapter::Header request;
- request.code = 1;
- request.controllerId = kNonController;
- request.dataSize = 0;
-
- SResponse response;
- if (!hciAdapter.sendCommand(request, response, sizeof(response)))
- {
- Logger::warn(SSTR << " + Failed to get version information");
- return -1;
- }
-
- response.toHost();
-
- Logger::debug(SSTR << " + Version response has version=" << Utils::hex(response.version) << " and revision=" << Utils::hex(response.revision));
- return (response.version << 16) | response.revision;
-}
-
-// Returns information about the controller (address, name, current settings, etc.)
-//
-// See the definition for MgmtControllerInformation for details.
-//
-// ... or nullptr on error
-Mgmt::ControllerInformation *Mgmt::getControllerInformation()
-{
- struct SResponse : HciAdapter::ResponseEvent
- {
- ControllerInformation info;
-
- void toHost()
- {
- info.toHost();
- }
- } __attribute__((packed));
-
- HciAdapter::Header request;
- request.code = 4; // Controller Information Command
- request.controllerId = controllerIndex;
- request.dataSize = 0;
-
- Logger::debug("Dumping device information after configuration...");
-
- SResponse response;
- if (!hciAdapter.sendCommand(request, response, sizeof(response)))
- {
- Logger::warn(SSTR << " + Failed to get current settings");
- return nullptr;
- }
-
- response.toHost();
-
- // Copy it to our local
- controllerInfo = response.info;
-
- Logger::debug(" + Controller information");
- Logger::debug(SSTR << " + Current settings : " << Utils::hex(controllerInfo.currentSettings));
- Logger::debug(SSTR << " + Address : " << Utils::bluetoothAddressString(controllerInfo.address));
- Logger::debug(SSTR << " + BT Version : " << controllerInfo.bluetoothVersion);
- Logger::debug(SSTR << " + Manufacturer : " << Utils::hex(controllerInfo.manufacturer));
- Logger::debug(SSTR << " + Supported settings : " << controllerSettingsString(controllerInfo.supportedSettings));
- Logger::debug(SSTR << " + Current settings : " << controllerSettingsString(controllerInfo.currentSettings));
- Logger::debug(SSTR << " + Name : " << controllerInfo.name);
- Logger::debug(SSTR << " + Short name : " << controllerInfo.shortName);
-
- return &controllerInfo;
+ HciAdapter::getInstance().sync(controllerIndex);
}
// Set the adapter name and short name
@@ -149,22 +64,16 @@ bool Mgmt::setName(std::string name, std::string shortName)
name = truncateName(name);
shortName = truncateShortName(shortName);
- struct SRequest : HciAdapter::Header
- {
- char name[249];
- char shortName[11];
- } __attribute__((packed));
-
- struct SResponse : HciAdapter::ResponseEvent
+ struct SRequest : HciAdapter::HciHeader
{
char name[249];
char shortName[11];
} __attribute__((packed));
SRequest request;
- request.code = 0x000F;
+ request.code = Mgmt::ESetLocalNameCommand;
request.controllerId = controllerIndex;
- request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::Header);
+ request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::HciHeader);
memset(request.name, 0, sizeof(request.name));
snprintf(request.name, sizeof(request.name), "%s", name.c_str());
@@ -172,14 +81,12 @@ bool Mgmt::setName(std::string name, std::string shortName)
memset(request.shortName, 0, sizeof(request.shortName));
snprintf(request.shortName, sizeof(request.shortName), "%s", shortName.c_str());
- SResponse response;
- if (!hciAdapter.sendCommand(request, response, sizeof(response)))
+ if (!HciAdapter::getInstance().sendCommand(request))
{
Logger::warn(SSTR << " + Failed to set name");
return false;
}
- Logger::info(SSTR << " + Name set to '" << request.name << "', short name set to '" << request.shortName << "'");
return true;
}
@@ -188,39 +95,25 @@ bool Mgmt::setName(std::string name, std::string shortName)
// Many settings are set the same way, this is just a convenience routine to handle them all
//
// Returns true on success, otherwise false
-bool Mgmt::setState(const char *pSettingName, uint16_t commandCode, uint16_t controllerId, uint8_t newState)
+bool Mgmt::setState(uint16_t commandCode, uint16_t controllerId, uint8_t newState)
{
- struct SRequest : HciAdapter::Header
+ struct SRequest : HciAdapter::HciHeader
{
uint8_t state;
} __attribute__((packed));
- struct SResponse : HciAdapter::ResponseEvent
- {
- uint32_t currentSettings;
-
- void toHost()
- {
- currentSettings = Utils::endianToHost(currentSettings);
- }
- } __attribute__((packed));
-
SRequest request;
request.code = commandCode;
request.controllerId = controllerId;
- request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::Header);
+ request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::HciHeader);
request.state = newState;
- SResponse response;
- if (!hciAdapter.sendCommand(request, response, sizeof(response)))
+ if (!HciAdapter::getInstance().sendCommand(request))
{
- Logger::warn(SSTR << " + Failed to set " << pSettingName << " state to: " << static_cast<int>(newState));
+ Logger::warn(SSTR << " + Failed to set " << HciAdapter::kCommandCodeNames[commandCode] << " state to: " << static_cast<int>(newState));
return false;
}
- response.toHost();
-
- Logger::debug(SSTR << " + " << pSettingName << " set to " << static_cast<int>(newState) << ": " << controllerSettingsString(response.currentSettings));
return true;
}
@@ -229,7 +122,7 @@ bool Mgmt::setState(const char *pSettingName, uint16_t commandCode, uint16_t con
// Returns true on success, otherwise false
bool Mgmt::setPowered(bool newState)
{
- return setState("Powered", 0x0005, controllerIndex, newState ? 1 : 0);
+ return setState(Mgmt::ESetPoweredCommand, controllerIndex, newState ? 1 : 0);
}
// Set the BR/EDR state to `newState` (true = enabled, false = disabled)
@@ -237,7 +130,7 @@ bool Mgmt::setPowered(bool newState)
// Returns true on success, otherwise false
bool Mgmt::setBredr(bool newState)
{
- return setState("BR/EDR", 0x002A, controllerIndex, newState ? 1 : 0);
+ return setState(Mgmt::ESetBREDRCommand, controllerIndex, newState ? 1 : 0);
}
// Set the Secure Connection state (0 = disabled, 1 = enabled, 2 = secure connections only mode)
@@ -245,7 +138,7 @@ bool Mgmt::setBredr(bool newState)
// Returns true on success, otherwise false
bool Mgmt::setSecureConnections(uint8_t newState)
{
- return setState("SecureConnections", 0x002D, controllerIndex, newState);
+ return setState(Mgmt::ESetSecureConnectionsCommand, controllerIndex, newState);
}
// Set the bondable state to `newState` (true = enabled, false = disabled)
@@ -253,7 +146,7 @@ bool Mgmt::setSecureConnections(uint8_t newState)
// Returns true on success, otherwise false
bool Mgmt::setBondable(bool newState)
{
- return setState("SecureConnections", 0x0009, controllerIndex, newState ? 1 : 0);
+ return setState(Mgmt::ESetBondableCommand, controllerIndex, newState ? 1 : 0);
}
// Set the connectable state to `newState` (true = enabled, false = disabled)
@@ -261,7 +154,7 @@ bool Mgmt::setBondable(bool newState)
// Returns true on success, otherwise false
bool Mgmt::setConnectable(bool newState)
{
- return setState("Connectable", 0x0007, controllerIndex, newState ? 1 : 0);
+ return setState(Mgmt::ESetConnectableCommand, controllerIndex, newState ? 1 : 0);
}
// Set the LE state to `newState` (true = enabled, false = disabled)
@@ -269,7 +162,7 @@ bool Mgmt::setConnectable(bool newState)
// Returns true on success, otherwise false
bool Mgmt::setLE(bool newState)
{
- return setState("LowEnergy", 0x000D, controllerIndex, newState ? 1 : 0);
+ return setState(Mgmt::ESetLowEnergyCommand, controllerIndex, newState ? 1 : 0);
}
// Set the advertising state to `newState` (0 = disabled, 1 = enabled (with consideration towards the connectable setting),
@@ -278,44 +171,13 @@ bool Mgmt::setLE(bool newState)
// Returns true on success, otherwise false
bool Mgmt::setAdvertising(uint8_t newState)
{
- return setState("Advertising", 0x0029, controllerIndex, newState);
+ return setState(Mgmt::ESetAdvertisingCommand, controllerIndex, newState);
}
// ---------------------------------------------------------------------------------------------------------------------------------
// Utilitarian
// ---------------------------------------------------------------------------------------------------------------------------------
-// Transforms a "Current_Settings" value (4 octets as defined by the Bluetooth Management API specification) into a human-readable
-// string of flags and settings.
-std::string Mgmt::controllerSettingsString(uint32_t bits)
-{
- std::string result = "";
-
- if ((bits & EHciPowered) != 0) { result += "Powered, "; }
- if ((bits & EHciConnectable) != 0) { result += "Connectable, "; }
- if ((bits & EHciFastConnectable) != 0) { result += "FC, "; }
- if ((bits & EHciDiscoverable) != 0) { result += "Discov, "; }
- if ((bits & EHciBondable) != 0) { result += "Bondable, "; }
- if ((bits & EHciLinkLevelSecurity) != 0) { result += "LLS, "; }
- if ((bits & EHciSecureSimplePairing) != 0) { result += "SSP, "; }
- if ((bits & EHciBasicRate_EnhancedDataRate) != 0) { result += "BR/EDR, "; }
- if ((bits & EHciHighSpeed) != 0) { result += "HS, "; }
- if ((bits & EHciLowEnergy) != 0) { result += "LE, "; }
- if ((bits & EHciAdvertising) != 0) { result += "Adv, "; }
- if ((bits & EHciSecureConnections) != 0) { result += "SC, "; }
- if ((bits & EHciDebugKeys) != 0) { result += "DebugKeys, "; }
- if ((bits & EHciPrivacy) != 0) { result += "Privacy, "; }
- if ((bits & EHciControllerConfiguration) != 0) { result += "ControllerConfig, "; }
- if ((bits & EHciStaticAddress) != 0) { result += "StaticAddr, "; }
-
- if (result.length() != 0)
- {
- result = result.substr(0, result.length() - 2);
- }
-
- return result;
-}
-
// Truncates the string `name` to the maximum allowed length for an adapter name. If `name` needs no truncation, a copy of
// `name` is returned.
std::string Mgmt::truncateName(const std::string &name)
diff --git a/src/Mgmt.h b/src/Mgmt.h
index 23315a5..196eb1a 100644
--- a/src/Mgmt.h
+++ b/src/Mgmt.h
@@ -59,49 +59,121 @@ struct Mgmt
// Types
//
- // HCI Controller Settings
- enum EHciControllerSettings
+ // These indices should match those in HciAdapter::kEventTypeNames
+ enum EventTypes
{
- 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)
+ EInvalidEvent = 0x0000,
+ ECommandCompleteEvent = 0x0001,
+ ECommandStatusEvent = 0x0002,
+ EControllerErrorEvent = 0x0003,
+ EIndexAddedEvent = 0x0004,
+ EIndexRemovedEvent = 0x0005,
+ ENewSettingsEvent = 0x0006,
+ EClassOfDeviceChangedEvent = 0x0007,
+ ELocalNameChangedEvent = 0x0008,
+ ENewLinkKeyEvent = 0x0009,
+ ENewLongTermKeyEvent = 0x000A,
+ EDeviceConnectedEvent = 0x000B,
+ EDeviceDisconnectedEvent = 0x000C,
+ EConnectFailedEvent = 0x000D,
+ EPINCodeRequestEvent = 0x000E,
+ EUserConfirmationRequestEvent = 0x000F,
+ EUserPasskeyRequestEvent = 0x0010,
+ EAuthenticationFailedEvent = 0x0011,
+ EDeviceFoundEvent = 0x0012,
+ EDiscoveringEvent = 0x0013,
+ EDeviceBlockedEvent = 0x0014,
+ EDeviceUnblockedEvent = 0x0015,
+ EDeviceUnpairedEvent = 0x0016,
+ EPasskeyNotifyEvent = 0x0017,
+ ENewIdentityResolvingKeyEvent = 0x0018,
+ ENewSignatureResolvingKeyEvent = 0x0019,
+ EDeviceAddedEvent = 0x001a,
+ EDeviceRemovedEvent = 0x001b,
+ ENewConnectionParameterEvent = 0x001c,
+ EUnconfiguredIndexAddedEvent = 0x001d,
+ EUnconfiguredIndexRemovedEvent = 0x001e,
+ ENewConfigurationOptionsEvent = 0x001f,
+ EExtendedIndexAddedEvent = 0x0020,
+ EExtendedIndexRemovedEvent = 0x0021,
+ ELocalOutOfBandExtendedDataUpdatedEvent = 0x0022,
+ EAdvertisingAddedEvent = 0x0023,
+ EAdvertisingRemovedEvent = 0x0024,
+ EExtendedControllerInformationChangedEvent = 0x0025
};
- // 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
+ // These indices should match those in HciAdapter::kCommandCodeNames
+ enum CommandCodes
{
- uint8_t address[6]; // The Bluetooth address
- uint8_t bluetoothVersion; // Bluetooth version
- uint16_t manufacturer; // The manufacturer
- uint32_t supportedSettings; // Bits for various supported settings (see EHciControllerSettings)
- uint32_t currentSettings; // Bits for various currently configured settings (see EHciControllerSettings)
- 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 = Utils::endianToHost(supportedSettings);
- currentSettings = Utils::endianToHost(currentSettings);
- }
- } __attribute__((packed));
+ EInvalidCommand = 0x0000,
+ EReadVersionInformationCommand = 0x0001,
+ EReadSupportedCommandsCommand = 0x0002,
+ EReadControllerIndexListCommand = 0x0003,
+ EReadControllerInformationCommand = 0x0004,
+ ESetPoweredCommand = 0x0005,
+ ESetDiscoverableCommand = 0x0006,
+ ESetConnectableCommand = 0x0007,
+ ESetFastConnectableCommand = 0x0008,
+ ESetBondableCommand = 0x0009,
+ ESetLinkSecurityCommand = 0x000A,
+ ESetSecureSimplePairingCommand = 0x000B,
+ ESetHighSpeedCommand = 0x000C,
+ ESetLowEnergyCommand = 0x000D,
+ ESetDeviceClass = 0x000E,
+ ESetLocalNameCommand = 0x000F,
+ EAddUUIDCommand = 0x0010,
+ ERemoveUUIDCommand = 0x0011,
+ ELoadLinkKeysCommand = 0x0012,
+ ELoadLongTermKeysCommand = 0x0013,
+ EDisconnectCommand = 0x0014,
+ EGetConnectionsCommand = 0x0015,
+ EPINCodeReplyCommand = 0x0016,
+ EPINCodeNegativeReplyCommand = 0x0017,
+ ESetIOCapabilityCommand = 0x0018,
+ EPairDeviceCommand = 0x0019,
+ ECancelPairDeviceCommand = 0x001A,
+ EUnpairDeviceCommand = 0x001B,
+ EUserConfirmationReplyCommand = 0x001C,
+ EUserConfirmationNegativeReplyCommand = 0x001D,
+ EUserPasskeyReplyCommand = 0x001E,
+ EUserPasskeyNegativeReplyCommand = 0x001F,
+ EReadLocalOutOfBandDataCommand = 0x0020,
+ EAddRemoteOutOfBandDataCommand = 0x0021,
+ ERemoveRemoteOutOfBandDataCommand = 0x0022,
+ EStartDiscoveryCommand = 0x0023,
+ EStopDiscoveryCommand = 0x0024,
+ EConfirmNameCommand = 0x0025,
+ EBlockDeviceCommand = 0x0026,
+ EUnblockDeviceCommand = 0x0027,
+ ESetDeviceIDCommand = 0x0028,
+ ESetAdvertisingCommand = 0x0029,
+ ESetBREDRCommand = 0x002A,
+ ESetStaticAddressCommand = 0x002B,
+ ESetScanParametersCommand = 0x002C,
+ ESetSecureConnectionsCommand = 0x002D,
+ ESetDebugKeysCommand = 0x002E,
+ ESetPrivacyCommand = 0x002F,
+ ELoadIdentityResolvingKeysCommand = 0x0030,
+ EGetConnectionInformationCommand = 0x0031,
+ EGetClockInformationCommand = 0x0032,
+ EAddDeviceCommand = 0x0033,
+ ERemoveDeviceCommand = 0x0034,
+ ELoadConnectionParametersCommand = 0x0035,
+ EReadUnconfiguredControllerIndexListCommand = 0x0036,
+ EReadControllerConfigurationInformationCommand = 0x0037,
+ ESetExternalConfigurationCommand = 0x0038,
+ ESetPublicAddressCommand = 0x0039,
+ EStartServiceDiscoveryCommand = 0x003a,
+ EReadLocalOutOfBandExtendedDataCommand = 0x003b,
+ EReadExtendedControllerIndexListCommand = 0x003c,
+ EReadAdvertisingFeaturesCommand = 0x003d,
+ EAddAdvertisingCommand = 0x003e,
+ ERemoveAdvertisingCommand = 0x003f,
+ EGetAdvertisingSizeInformationCommand = 0x0040,
+ EStartLimitedDiscoveryCommand = 0x0041,
+ EReadExtendedControllerInformationCommand = 0x0042,
+ ESetAppearanceCommand = 0x0043
+ };
// Construct the Mgmt device
//
@@ -109,21 +181,6 @@ struct Mgmt
// of the first device (0) will be used.
Mgmt(uint16_t controllerIndex = kDefaultControllerIndex);
- // Returns the version information:
- //
- // bits 0-15 = revision
- // bits 16-23 = version
- //
- // ... or -1 on error
- int getVersion();
-
- // Returns information about the controller (address, name, current settings, etc.)
- //
- // See the definition for MgmtControllerInformation for details.
- //
- // ... or nullptr on error
- ControllerInformation *getControllerInformation();
-
// Set the adapter name and short name
//
// The inputs `name` and `shortName` may be truncated prior to setting them on the adapter. To ensure that `name` and
@@ -138,7 +195,7 @@ struct Mgmt
// Many settings are set the same way, this is just a convenience routine to handle them all
//
// Returns true on success, otherwise false
- bool setState(const char *pSettingName, uint16_t commandCode, uint16_t controllerId, uint8_t newState);
+ bool setState(uint16_t commandCode, uint16_t controllerId, uint8_t newState);
// Set the powered state to `newState` (true = powered on, false = powered off)
//
@@ -180,10 +237,6 @@ struct Mgmt
// Utilitarian
//
- // Transforms a "Current_Settings" value (4 octets as defined by the Bluetooth Management API specification) into a human-
- // readable string of flags and settings.
- static std::string controllerSettingsString(uint32_t bits);
-
// Truncates the string `name` to the maximum allowed length for an adapter name. If `name` needs no truncation, a copy of
// `name` is returned.
static std::string truncateName(const std::string &name);
@@ -198,20 +251,11 @@ private:
// Data members
//
- // Our adapter - we use this to connect to the HCI socket to talk to the adapter
- HciAdapter hciAdapter;
-
- // Our controller information, updated each time the controller information is received
- ControllerInformation controllerInfo;
-
// The default controller index (the first device)
uint16_t controllerIndex;
// Default controller index
static const uint16_t kDefaultControllerIndex = 0;
-
- // A constant referring to a 'non-controller' (for commands that do not require a controller index)
- static const uint16_t kNonController = 0xffff;
};
}; // namespace ggk \ No newline at end of file
diff --git a/src/TickEvent.h b/src/TickEvent.h
index e72ae9b..146f670 100644
--- a/src/TickEvent.h
+++ b/src/TickEvent.h
@@ -129,7 +129,7 @@ struct TickEvent
{
if (nullptr != callback)
{
- Logger::info(SSTR << "Ticking at path '" << path << "'");
+ Logger::debug(SSTR << "Ticking at path '" << path << "'");
callback(*static_cast<const T *>(pOwner), *this, pConnection, pUserData);
}