aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Gobbledegook.cpp52
-rw-r--r--src/HciAdapter.cpp203
-rw-r--r--src/HciAdapter.h82
-rw-r--r--src/HciSocket.cpp62
-rw-r--r--src/HciSocket.h6
-rw-r--r--src/Init.cpp154
6 files changed, 336 insertions, 223 deletions
diff --git a/src/Gobbledegook.cpp b/src/Gobbledegook.cpp
index 5fa0fe8..e5d8703 100644
--- a/src/Gobbledegook.cpp
+++ b/src/Gobbledegook.cpp
@@ -87,9 +87,10 @@ namespace ggk
}
// Internal method to set the health of the server
- void setServerHealth(GGKServerHealth newState)
+ void setServerHealth(GGKServerHealth newHealth)
{
- serverHealth = newState;
+ Logger::status(SSTR << "** SERVER HEALTH CHANGED: " << ggkGetServerHealthString(serverHealth) << " -> " << ggkGetServerHealthString(newHealth));
+ serverHealth = newHealth;
}
}; // namespace ggk
@@ -255,7 +256,7 @@ const char *ggkGetServerRunStateString(GGKServerRunState state)
// Convenience method to check ServerRunState for a running server
int ggkIsServerRunning()
{
- return serverRunState == ERunning ? 1 : 0;
+ return serverRunState <= ERunning ? 1 : 0;
}
// ---------------------------------------------------------------------------------------------------------------------------------
@@ -344,7 +345,7 @@ int ggkWait()
int result = 0;
try
{
- Logger::info("Stopping GGK server");
+ Logger::info("Waiting for GGK server to stop");
serverThread.join();
result = 1;
@@ -353,19 +354,19 @@ int ggkWait()
{
if (ex.code() == std::errc::invalid_argument)
{
- Logger::warn(SSTR << "Server thread was not joinable during stop(): " << ex.what());
+ Logger::warn(SSTR << "Server thread was not joinable during ggkWait(): " << ex.what());
}
else if (ex.code() == std::errc::no_such_process)
{
- Logger::warn(SSTR << "Server thread was not valid during stop(): " << ex.what());
+ Logger::warn(SSTR << "Server thread was not valid during ggkWait(): " << ex.what());
}
else if (ex.code() == std::errc::resource_deadlock_would_occur)
{
- Logger::warn(SSTR << "Deadlock avoided in call to stop() (did the server thread try to stop itself?): " << ex.what());
+ Logger::warn(SSTR << "Deadlock avoided in call to ggkWait() (did the server thread try to stop itself?): " << ex.what());
}
else
{
- Logger::warn(SSTR << "Unknown system_error code (" << ex.code() << "): " << ex.what());
+ Logger::warn(SSTR << "Unknown system_error code (" << ex.code() << ") during ggkWait(): " << ex.what());
}
}
@@ -440,6 +441,8 @@ int ggkWait()
int ggkStart(const char *pServiceName, const char *pAdvertisingName, const char *pAdvertisingShortName,
GGKServerDataGetter getter, GGKServerDataSetter setter, int maxAsyncInitTimeoutMS)
{
+try
+{
//
// Start by capturing the GLib output
//
@@ -490,13 +493,10 @@ int ggkStart(const char *pServiceName, const char *pAdvertisingName, const char
}
catch(std::system_error &ex)
{
- if (ex.code() == std::errc::resource_unavailable_try_again)
- {
- Logger::error(SSTR << "Server thread was unable to start during start(): " << ex.what());
+ Logger::error(SSTR << "Server thread was unable to start (code " << ex.code() << ") during ggkStart(): " << ex.what());
- setServerRunState(EStopped);
- return 0;
- }
+ setServerRunState(EStopped);
+ return 0;
}
// Waits for the server to pass the EInitializing state
@@ -507,20 +507,34 @@ int ggkStart(const char *pServiceName, const char *pAdvertisingName, const char
retryTimeMS += kMaxAsyncInitCheckIntervalMS;
}
- // If something went wrong, shut down if we've not already done so
- if (ggkGetServerRunState() < ERunning || ggkGetServerHealth() != EOk)
+ // If something went wrong, shut down
+ if (retryTimeMS >= maxAsyncInitTimeoutMS)
{
+ Logger::error("GGK server initialization timed out");
+
setServerHealth(EFailedInit);
- // Stop the server (this is a blocking call)
- if (!ggkShutdownAndWait())
+ shutdown();
+ }
+
+ // If something went wrong, shut down if we've not already done so
+ if (ggkGetServerRunState() != ERunning)
+ {
+ if (!ggkWait())
{
- Logger::warn(SSTR << "Unable to stop the server from error in start()");
+ Logger::warn(SSTR << "Unable to stop the server after an error in ggkStart()");
}
return 0;
}
// Everything looks good
+ Logger::trace("GGK server has started");
return 1;
+ }
+catch(...)
+{
+ Logger::error(SSTR << "Unknown exception during ggkStart()");
+ return 0;
+}
}
diff --git a/src/HciAdapter.cpp b/src/HciAdapter.cpp
index 06c4457..683d6e2 100644
--- a/src/HciAdapter.cpp
+++ b/src/HciAdapter.cpp
@@ -53,6 +53,7 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <string.h>
+#include <chrono>
#include "HciAdapter.h"
#include "HciSocket.h"
@@ -208,26 +209,6 @@ const char * const HciAdapter::kStatusCodes[kMaxStatusCode + 1] =
"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()
{
@@ -237,11 +218,13 @@ void 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
+//
+// It isn't necessary to disconnect manually; the HCI socket will get disocnnected automatically at before this method returns
void HciAdapter::runEventThread()
{
Logger::trace("Entering the HciAdapter event thread");
- while (ggkGetServerRunState() <= ERunning)
+ while (ggkGetServerRunState() <= ERunning && hciSocket.isConnected())
{
// Read the next event, waiting until one arrives
std::vector<uint8_t> responsePacket = std::vector<uint8_t>();
@@ -273,13 +256,7 @@ void HciAdapter::runEventThread()
case Mgmt::ECommandCompleteEvent:
{
// Extract our event
- CommandCompleteEvent event = *reinterpret_cast<CommandCompleteEvent *>(responsePacket.data());
-
- // Fixup endian
- event.toHost();
-
- // Log it
- Logger::debug(event.debugText());
+ CommandCompleteEvent event(responsePacket);
// Point to the data following the event
uint8_t *data = responsePacket.data() + sizeof(CommandCompleteEvent);
@@ -348,54 +325,40 @@ void HciAdapter::runEventThread()
break;
}
}
+
+ // Notify anybody waiting that we received a response to their command code
+ if (conditionalValue == event.commandCode)
+ {
+ conditionalWait.notify_one();
+ }
+
break;
}
// Command status event
case Mgmt::ECommandStatusEvent:
{
- // Extract our event
- CommandStatusEvent event = *reinterpret_cast<CommandStatusEvent *>(responsePacket.data());
+ CommandStatusEvent event(responsePacket);
- // Fixup endian
- event.toHost();
-
- // Log it
- Logger::debug(event.debugText());
+ // Notify anybody waiting that we received a response to their command code
+ if (conditionalValue == event.commandCode)
+ {
+ conditionalWait.notify_one();
+ }
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
+ DeviceConnectedEvent event(responsePacket);
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
+ DeviceDisconnectedEvent event(responsePacket);
activeConnections -= 1;
-
break;
}
// Unsupported
@@ -413,6 +376,9 @@ void HciAdapter::runEventThread()
}
}
+ // Make sure we're disconnected before we leave
+ hciSocket.disconnect();
+
Logger::trace("Leaving the HciAdapter event thread");
}
@@ -422,6 +388,8 @@ void HciAdapter::runEventThread()
// milliseconds. Therefore, it is not recommended attempt to retrieve the results from their accessors immediately.
void HciAdapter::sync(uint16_t controllerIndex)
{
+ Logger::debug("Synchronizing version information");
+
HciAdapter::HciHeader request;
request.code = Mgmt::EReadVersionInformationCommand;
request.controllerId = HciAdapter::kNonController;
@@ -432,6 +400,8 @@ void HciAdapter::sync(uint16_t controllerIndex)
Logger::error("Failed to get version information");
}
+ Logger::debug("Synchronizing controller information");
+
request.code = Mgmt::EReadControllerInformationCommand;
request.controllerId = controllerIndex;
request.dataSize = 0;
@@ -442,30 +412,31 @@ void HciAdapter::sync(uint16_t controllerIndex)
}
}
-// Connects the HCI socket if a connection does not already exist
+// Connects the HCI socket if a connection does not already exist and starts the run thread
//
-// If a connection already exists, this method will do nothing and return true.
+// If the thread is already running, this method will fail
//
// Note that it shouldn't be necessary to connect manually; any action requiring a connection will automatically connect
//
// Returns true if the HCI socket is connected (either via a new connection or an existing one), otherwise false
-bool HciAdapter::connect()
+bool HciAdapter::start()
{
- // Already connected?
- if (isConnected())
+ // If the thread is already running, return failure
+ if (eventThread.joinable())
{
- return true;
+ return false;
}
- // Try to connect
- if (!hciSocket.connect())
+ // Already connected?
+ if (!hciSocket.isConnected())
{
- disconnect();
- return false;
+ // Connect
+ if (!hciSocket.connect())
+ {
+ return false;
+ }
}
- Logger::trace("Starting the HciAdapter thread");
-
// Create a thread to read the data from the socket
try
{
@@ -473,43 +444,51 @@ bool HciAdapter::connect()
}
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;
- }
+ Logger::error(SSTR << "HciAdapter thread was unable to start (code " << ex.code() << "): " << ex.what());
+ return false;
}
return true;
}
-// Returns true if connected to the HCI socket, otherwise false
+// Waits for the HciAdapter run thread to join
//
-// Note that it shouldn't be necessary to connect manually; any action requiring a connection will automatically connect
-bool HciAdapter::isConnected() const
+// This method will block until the thread joins
+void HciAdapter::stop()
{
- return hciSocket.isConnected();
-}
+ Logger::trace("HciAdapter waiting for thread termination");
-// Disconnects from the HCI Socket
-//
-// If the connection is not connected, this method will do nothing.
-//
-// It isn't necessary to disconnect manually; the HCI socket will get disocnnected automatically upon destruction
-void HciAdapter::disconnect()
-{
- if (isConnected())
+ try
{
- hciSocket.disconnect();
- }
+ if (eventThread.joinable())
+ {
+ eventThread.join();
- if (eventThread.joinable())
+ Logger::trace("Event thread has stopped");
+ }
+ else
+ {
+ Logger::trace(" > Event thread is not joinable");
+ }
+ }
+ catch(std::system_error &ex)
{
- Logger::trace("Stopping the HciAdapter thread");
-
- pthread_kill(eventThread.native_handle(), SIGINT);
- eventThread.join();
+ if (ex.code() == std::errc::invalid_argument)
+ {
+ Logger::warn(SSTR << "HciAdapter event thread was not joinable during HciAdapter::wait(): " << ex.what());
+ }
+ else if (ex.code() == std::errc::no_such_process)
+ {
+ Logger::warn(SSTR << "HciAdapter event was not valid during HciAdapter::wait(): " << ex.what());
+ }
+ else if (ex.code() == std::errc::resource_deadlock_would_occur)
+ {
+ Logger::warn(SSTR << "Deadlock avoided in call to HciAdapter::wait() (did the event thread try to stop itself?): " << ex.what());
+ }
+ else
+ {
+ Logger::warn(SSTR << "Unknown system_error code (" << ex.code() << ") during HciAdapter::wait(): " << ex.what());
+ }
}
}
@@ -522,9 +501,11 @@ void HciAdapter::disconnect()
bool HciAdapter::sendCommand(HciHeader &request)
{
// Auto-connect
- if (!connect()) { return false; }
-
- Logger::debug(request.debugText());
+ if (!eventThread.joinable() && !start())
+ {
+ Logger::error("HciAdapter failed to start");
+ return false;
+ }
request.toNetwork();
uint8_t *pRequest = reinterpret_cast<uint8_t *>(&request);
@@ -534,9 +515,31 @@ bool HciAdapter::sendCommand(HciHeader &request)
return false;
}
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ return waitFor(request.code, kMaxEventWaitTimeMS);
+}
- return true;
+// Uses a std::condition_variable to wait for a response event for the given `commandCode` or `timeoutMS` milliseconds.
+//
+// Returns true if the response event was received for `commandCode` or false if the timeout expired.
+bool HciAdapter::waitFor(uint16_t commandCode, int timeoutMS)
+{
+ std::mutex mtx;
+ std::unique_lock<std::mutex> lock(mtx);
+ conditionalValue = commandCode;
+
+ Logger::debug(SSTR << " + Waiting on command code " << commandCode << " for " << timeoutMS << "ms");
+ bool timedOut = conditionalWait.wait_for(lock, std::chrono::milliseconds(timeoutMS)) == std::cv_status::timeout;
+
+ if (timedOut)
+ {
+ Logger::warn(SSTR << "+ Timed out waiting on command code " << Utils::hex(commandCode) << " (" << kCommandCodeNames[commandCode] << ")");
+ }
+ else
+ {
+ Logger::debug(SSTR << "+ Recieved the command code we were waiting for: " << Utils::hex(commandCode) << " (" << kCommandCodeNames[commandCode] << ")");
+ }
+
+ return !timedOut;
}
}; // namespace ggk \ No newline at end of file
diff --git a/src/HciAdapter.h b/src/HciAdapter.h
index a19d395..ad4f62a 100644
--- a/src/HciAdapter.h
+++ b/src/HciAdapter.h
@@ -37,9 +37,12 @@
#include <stdint.h>
#include <vector>
#include <thread>
+#include <mutex>
+#include <condition_variable>
#include "HciSocket.h"
#include "Utils.h"
+#include "Logger.h"
namespace ggk {
@@ -51,6 +54,9 @@ public:
// Constants
//
+ // How long to wait for a response event for commands sent to the adapter
+ static const int kMaxEventWaitTimeMS = 1000;
+
// A constant referring to a 'non-controller' (for commands that do not require a controller index)
static const uint16_t kNonController = 0xffff;
@@ -130,6 +136,15 @@ public:
uint16_t commandCode;
uint8_t status;
+ CommandCompleteEvent(const std::vector<uint8_t> &data)
+ {
+ *this = *reinterpret_cast<const CommandCompleteEvent *>(data.data());
+ toHost();
+
+ // Log it
+ Logger::debug(debugText());
+ }
+
void toNetwork()
{
header.toNetwork();
@@ -161,6 +176,15 @@ public:
uint16_t commandCode;
uint8_t status;
+ CommandStatusEvent(const std::vector<uint8_t> &data)
+ {
+ *this = *reinterpret_cast<const CommandStatusEvent *>(data.data());
+ toHost();
+
+ // Log it
+ Logger::debug(debugText());
+ }
+
void toNetwork()
{
header.toNetwork();
@@ -194,6 +218,15 @@ public:
uint32_t flags;
uint16_t eirDataLength;
+ DeviceConnectedEvent(const std::vector<uint8_t> &data)
+ {
+ *this = *reinterpret_cast<const DeviceConnectedEvent *>(data.data());
+ toHost();
+
+ // Log it
+ Logger::debug(debugText());
+ }
+
void toNetwork()
{
header.toNetwork();
@@ -235,6 +268,15 @@ public:
uint8_t addressType;
uint8_t reason;
+ DeviceDisconnectedEvent(const std::vector<uint8_t> &data)
+ {
+ *this = *reinterpret_cast<const DeviceDisconnectedEvent *>(data.data());
+ toHost();
+
+ // Log it
+ Logger::debug(debugText());
+ }
+
void toNetwork()
{
header.toNetwork();
@@ -378,8 +420,8 @@ public:
{
std::string text = "";
text += "> Local name information\n";
- text += " + Name : '" + std::string(name) + "\n";
- text += " + Short name : '" + std::string(shortName);
+ text += " + Name : '" + std::string(name) + "'\n";
+ text += " + Short name : '" + std::string(shortName) + "'";
return text;
}
} __attribute__((packed));
@@ -395,11 +437,10 @@ public:
return instance;
}
- AdapterSettings getAdapterSettings();
- ControllerInformation getControllerInformation();
- VersionInformation getVersionInformation();
- LocalName getLocalName();
-
+ AdapterSettings getAdapterSettings() { return adapterSettings; }
+ ControllerInformation getControllerInformation() { return controllerInformation; }
+ VersionInformation getVersionInformation() { return versionInformation; }
+ LocalName getLocalName() { return localName; }
int getActiveConnectionCount() { return activeConnections; }
//
@@ -415,26 +456,19 @@ public:
// 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
+ // Connects the HCI socket if a connection does not already exist and starts the run thread
//
- // If a connection already exists, this method will do nothing and return true.
+ // If a connection already exists, this method will fail
//
// Note that it shouldn't be necessary to connect manually; any action requiring a connection will automatically connect
//
// Returns true if the HCI socket is connected (either via a new connection or an existing one), otherwise false
- bool connect();
-
- // Returns true if connected to the HCI socket, otherwise false
- //
- // Note that it shouldn't be necessary to connect manually; any action requiring a connection will automatically connect
- bool isConnected() const;
+ bool start();
- // Disconnects from the HCI Socket
- //
- // If the connection is not connected, this method will do nothing.
+ // Waits for the HciAdapter run thread to join
//
- // It isn't necessary to disconnect manually; the HCI socket will get disocnnected automatically upon destruction
- void disconnect();
+ // This method will block until the thread joins
+ void stop();
// Sends a command over the HCI socket
//
@@ -453,6 +487,11 @@ private:
// Private constructor for our Singleton
HciAdapter() : activeConnections(0) {}
+ // Uses a std::condition_variable to wait for a response event for the given `commandCode` or `timeoutMS` milliseconds.
+ //
+ // Returns true if the response event was received for `commandCode` or false if the timeout expired.
+ bool waitFor(uint16_t commandCode, int timeoutMS);
+
// Our HCI Socket, which allows us to talk directly to the kernel
HciSocket hciSocket;
@@ -465,6 +504,9 @@ private:
VersionInformation versionInformation;
LocalName localName;
+ std::condition_variable conditionalWait;
+ int conditionalValue;
+
// Our active connection count
int activeConnections;
};
diff --git a/src/HciSocket.cpp b/src/HciSocket.cpp
index 214ef85..c3cadc3 100644
--- a/src/HciSocket.cpp
+++ b/src/HciSocket.cpp
@@ -92,7 +92,7 @@ bool HciSocket::connect()
return false;
}
- Logger::debug(SSTR << "Connected to HCI control socket (file descriptor = " << fdSocket << ")");
+ Logger::debug(SSTR << "Connected to HCI control socket (fd = " << fdSocket << ")");
return true;
}
@@ -108,8 +108,15 @@ void HciSocket::disconnect()
{
if (isConnected())
{
- close(fdSocket);
+ Logger::debug("HciSocket disconnecting");
+
+ if (close(fdSocket) != 0)
+ {
+ logErrno("close(fdSocket)");
+ }
+
fdSocket = -1;
+ Logger::trace("HciSocket closed");
}
}
@@ -124,8 +131,11 @@ bool HciSocket::read(std::vector<uint8_t> &response) const
// Fill our response with empty data
response.resize(kResponseMaxSize, 0);
- // Ensure we have blocking I/O
- fcntl(fdSocket, F_SETFL, 0);
+ // Wait for data or a cancellation
+ if (!waitForDataOrShutdown())
+ {
+ return false;
+ }
// Block until we receive data, a disconnect, or a signal
ssize_t bytesRead = ::recv(fdSocket, &response[0], kResponseMaxSize, MSG_WAITALL);
@@ -146,7 +156,7 @@ bool HciSocket::read(std::vector<uint8_t> &response) const
}
else if (bytesRead == 0)
{
- Logger::error(" + Peer closed the socket");
+ Logger::error("Peer closed the socket");
response.resize(0);
return false;
}
@@ -155,7 +165,7 @@ bool HciSocket::read(std::vector<uint8_t> &response) const
response.resize(bytesRead);
std::string dump = "";
- dump += "> Read " + std::to_string(response.size()) + " bytes\n";
+ dump += " > Read " + std::to_string(response.size()) + " bytes\n";
dump += Utils::hex(response.data(), response.size());
Logger::debug(dump);
@@ -176,7 +186,7 @@ bool HciSocket::write(std::vector<uint8_t> buffer) const
bool HciSocket::write(const uint8_t *pBuffer, size_t count) const
{
std::string dump = "";
- dump += "> Writing " + std::to_string(count) + " bytes\n";
+ dump += " > Writing " + std::to_string(count) + " bytes\n";
dump += Utils::hex(pBuffer, count);
Logger::debug(dump);
@@ -191,10 +201,46 @@ bool HciSocket::write(const uint8_t *pBuffer, size_t count) const
return true;
}
+// Wait for data to arrive, or for a shutdown event
+//
+// Returns true if data is available, false if we are shutting down
+bool HciSocket::waitForDataOrShutdown() const
+{
+ while(ggkIsServerRunning())
+ {
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(fdSocket, &rfds);
+
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = kDataWaitTimeMS * 1000;
+
+ int retval = select(fdSocket+1, &rfds, NULL, NULL, &tv);
+
+ // Do we have data?
+ if (retval > 0) { return true; }
+
+ // We have an error
+ if (retval < 0) { return false; }
+
+ // No data; keep waiting
+ continue;
+ }
+
+ return false;
+}
+
// Utilitarian function for logging errors for the given operation
void HciSocket::logErrno(const char *pOperation) const
{
- Logger::error(SSTR << "" << pOperation << " on Bluetooth management socket error (" << errno << "): " << strerror(errno));
+ std::string errorDetail(strerror(errno));
+ if (errno == EAGAIN)
+ {
+ errorDetail += " or not enough permission for this operation";
+ }
+
+ Logger::error(SSTR << "Error on Bluetooth management socket during " << pOperation << " operation. Error code " << errno << ": " << errorDetail);
}
}; // namespace ggk
diff --git a/src/HciSocket.h b/src/HciSocket.h
index 20dbe4e..b9eebb9 100644
--- a/src/HciSocket.h
+++ b/src/HciSocket.h
@@ -80,12 +80,18 @@ public:
private:
+ // Wait for data to arrive, or for a shutdown event
+ //
+ // Returns true if data is available, false if we are shutting down
+ bool waitForDataOrShutdown() const;
+
// Utilitarian function for logging errors for the given operation
void logErrno(const char *pOperation) const;
int fdSocket;
const size_t kResponseMaxSize = 64 * 1024;
+ const int kDataWaitTimeMS = 10;
};
}; // namespace ggk \ No newline at end of file
diff --git a/src/Init.cpp b/src/Init.cpp
index 49a4866..915209f 100644
--- a/src/Init.cpp
+++ b/src/Init.cpp
@@ -102,7 +102,7 @@ static std::string bluezGattManagerInterfaceName = "";
//
extern void setServerRunState(enum GGKServerRunState newState);
-extern void setServerHealth(enum GGKServerHealth newState);
+extern void setServerHealth(enum GGKServerHealth newHealth);
//
// Forward declarations
@@ -140,6 +140,7 @@ static void initializationStateProcessor();
// returning false when there is no work to do, we are nicer to the system.
bool idleFunc(void *pUserData)
{
+
// Don't do anything unless we're running
if (ggkGetServerRunState() != ERunning)
{
@@ -178,6 +179,7 @@ bool idleFunc(void *pUserData)
{
Logger::debug(SSTR << "Processing updated value for interface '" << interfaceName << "' at path '" << objectPath << "'");
pCharacteristic->callOnUpdatedValue(pBusConnection, pUserData);
+ return true;
}
}
@@ -290,16 +292,17 @@ void uninit()
// This method is non-blocking and as such, will only trigger the shutdown process but not wait for it
void shutdown()
{
- if (ggkGetServerRunState() >= EStopping)
+ if (ggkGetServerRunState() > ERunning)
{
- Logger::warn(SSTR << "shutdown() called while already shutting down");
+ Logger::warn("Ignoring call to shutdown (we are already shutting down)");
+ return;
}
// Our new state: shutting down
setServerRunState(EStopping);
- // Disconnect our HciAdapter
- HciAdapter::getInstance().disconnect();
+ // Stop our HciAdapter
+ HciAdapter::getInstance().stop();
// If we still have a main loop, ask it to quit
if (nullptr != pMainLoop)
@@ -323,6 +326,12 @@ void shutdown()
// failure retries, but custom code can also be added to a server description (see `onEvent()`)
gboolean onPeriodicTimer(gpointer pUserData)
{
+ // If we're shutting down, don't do anything and stop the periodic timer
+ if (ggkGetServerRunState() > ERunning)
+ {
+ return FALSE;
+ }
+
// Deal with retry timers
if (0 != retryTimeStart)
{
@@ -690,87 +699,76 @@ void configureAdapter()
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)
+ if (!pwFlag || !leFlag || !brFlag || !scFlag || !bnFlag || !cnFlag || !adFlag || !anFlag)
{
- 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 (pwFlag)
+ {
+ Logger::debug("Powering off");
+ if (!mgmt.setPowered(false)) { setRetry(); return; }
+ }
- // We need it off to start with
- if (pwFlag)
- {
- 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");
+ if (!mgmt.setLE(true)) { setRetry(); return; }
+ }
- // 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?
+ //
+ // 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())) { setRetry(); return; }
+ }
- // Change the Br/Edr state?
- //
- // 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");
- mgmt.setBredr(TheServer->getEnableBREDR());
- }
+ // Change the Secure Connectinos state?
+ if (!scFlag)
+ {
+ Logger::debug(SSTR << (TheServer->getEnableSecureConnection() ? "Enabling":"Disabling") << " Secure Connections");
+ if (!mgmt.setSecureConnections(TheServer->getEnableSecureConnection() ? 1 : 0)) { setRetry(); return; }
+ }
- // Change the Secure Connectinos state?
- if (!scFlag)
- {
- Logger::debug(SSTR << (TheServer->getEnableSecureConnection() ? "Enabling":"Disabling") << " Secure Connections");
- mgmt.setSecureConnections(TheServer->getEnableSecureConnection() ? 1 : 0);
- }
+ // Change the Bondable state?
+ if (!bnFlag)
+ {
+ Logger::debug(SSTR << (TheServer->getEnableBondable() ? "Enabling":"Disabling") << " Bondable");
+ if (!mgmt.setBondable(TheServer->getEnableBondable())) { setRetry(); return; }
+ }
- // Change the Bondable state?
- if (!bnFlag)
- {
- Logger::debug(SSTR << (TheServer->getEnableBondable() ? "Enabling":"Disabling") << " Bondable");
- mgmt.setBondable(TheServer->getEnableBondable());
- }
+ // Change the Connectable state?
+ if (!cnFlag)
+ {
+ Logger::debug(SSTR << (TheServer->getEnableConnectable() ? "Enabling":"Disabling") << " Connectable");
+ if (!mgmt.setConnectable(TheServer->getEnableConnectable())) { setRetry(); return; }
+ }
- // Change the Connectable state?
- if (!cnFlag)
- {
- Logger::debug(SSTR << (TheServer->getEnableConnectable() ? "Enabling":"Disabling") << " Connectable");
- mgmt.setConnectable(TheServer->getEnableConnectable());
- }
+ // Change the Advertising state?
+ if (!adFlag)
+ {
+ Logger::debug(SSTR << (TheServer->getEnableAdvertising() ? "Enabling":"Disabling") << " Advertising");
+ if (!mgmt.setAdvertising(TheServer->getEnableAdvertising() ? 1 : 0)) { setRetry(); return; }
+ }
- // Change the Advertising state?
- if (!adFlag)
- {
- Logger::debug(SSTR << (TheServer->getEnableAdvertising() ? "Enabling":"Disabling") << " Advertising");
- mgmt.setAdvertising(TheServer->getEnableAdvertising() ? 1 : 0);
- }
+ // Set the name?
+ if (!anFlag)
+ {
+ Logger::info(SSTR << "Setting advertising name to '" << advertisingName << "' (with short name: '" << advertisingShortName << "')");
+ if (!mgmt.setName(advertisingName.c_str(), advertisingShortName.c_str())) { setRetry(); return; }
+ }
- // Set the name?
- if (!anFlag)
- {
- 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("Powering on");
+ if (!mgmt.setPowered(true)) { setRetry(); return; }
}
- // Turn it back on
- 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));
+ Logger::info("The Bluetooth adapter is fully configured");
- // We can ignore errors on this - we're just letting it dump the output
- HciAdapter::getInstance().getControllerInformation();
-
- // 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();
+ // We're all set, nothing to do!
+ bAdapterConfigured = true;
+ initializationStateProcessor();
}
// ---------------------------------------------------------------------------------------------------------------------------------
@@ -1042,7 +1040,10 @@ void doBusAcquire()
void initializationStateProcessor()
{
// If we're in our end-of-life or waiting for a retry, don't process states
- if (ggkGetServerRunState() > ERunning || 0 != retryTimeStart) { return; }
+ if (ggkGetServerRunState() > ERunning || 0 != retryTimeStart)
+ {
+ return;
+ }
//
// Get a bus connection
@@ -1156,7 +1157,7 @@ void runServerThread()
// There are alternatives, but using async methods is the recommended way.
initializationStateProcessor();
- Logger::debug(SSTR << "Starting GLib main loop");
+ Logger::debug(SSTR << "Creating GLib main loop");
pMainLoop = g_main_loop_new(NULL, FALSE);
// Add the idle function
@@ -1184,6 +1185,7 @@ void runServerThread()
Logger::error(SSTR << "Unable to add idle to main loop");
}
+ Logger::trace(SSTR << "Starting GLib main loop");
g_main_loop_run(pMainLoop);
// We have stopped