From 6c3a7808db2ba7d3699aaa4c813da4eea810e577 Mon Sep 17 00:00:00 2001 From: Paul Nettle Date: Mon, 2 Oct 2017 11:02:09 -0500 Subject: Improved threading support, cleaner initialization process and added the ability to wait for threaded responses from HCI commands. --- src/Gobbledegook.cpp | 52 ++++++++----- src/HciAdapter.cpp | 203 ++++++++++++++++++++++++++------------------------- src/HciAdapter.h | 82 ++++++++++++++++----- src/HciSocket.cpp | 62 ++++++++++++++-- src/HciSocket.h | 6 ++ src/Init.cpp | 154 +++++++++++++++++++------------------- 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()); } } @@ -439,6 +440,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 +#include #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 responsePacket = std::vector(); @@ -273,13 +256,7 @@ void HciAdapter::runEventThread() case Mgmt::ECommandCompleteEvent: { // Extract our event - CommandCompleteEvent event = *reinterpret_cast(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(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(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(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(&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 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 #include #include +#include +#include #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 &data) + { + *this = *reinterpret_cast(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 &data) + { + *this = *reinterpret_cast(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 &data) + { + *this = *reinterpret_cast(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 &data) + { + *this = *reinterpret_cast(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 &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 &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 &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 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 -- cgit v1.2.3