From 06f646aec4dbce64d28bae1be6111bd833f8e79e Mon Sep 17 00:00:00 2001 From: Paul Nettle Date: Fri, 25 Aug 2017 09:30:39 -0500 Subject: Initial version 1.0 --- src/Mgmt.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 src/Mgmt.cpp (limited to 'src/Mgmt.cpp') diff --git a/src/Mgmt.cpp b/src/Mgmt.cpp new file mode 100644 index 0000000..d2ced63 --- /dev/null +++ b/src/Mgmt.cpp @@ -0,0 +1,339 @@ +// Copyright 2017 Paul Nettle. +// +// This file is part of Gobbledegook. +// +// Gobbledegook is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Gobbledegook is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Gobbledegook. If not, see . + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// >> +// >>> INSIDE THIS FILE +// >> +// +// This file contains various functions for interacting with Bluetooth Management interface, which provides adapter configuration. +// +// >> +// >>> DISCUSSION +// >> +// +// We only cover the basics here. If there are configuration features you need that aren't supported (such as configuring BR/EDR), +// then this would be a good place for them. +// +// Note that this class relies on the `HciAdapter`, which is a very primitive implementation. Use with caution. +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#include + +#include "Mgmt.h" +#include "Logger.h" +#include "Utils.h" + +// Construct the Mgmt device +// +// Set `controllerIndex` to the zero-based index of the device as recognized by the OS. If this parameter is omitted, the index +// of the first device (0) will be used. +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::info(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; +} + +// 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 +// `shortName` conform to length specifications prior to calling this method, see the constants `kMaxNameLength` and +// `kMaxShortNameLength`. In addition, the static methods `truncateName()` and `truncateShortName()` may be helpful. +// +// Returns true on success, otherwise false +bool Mgmt::setName(std::string name, std::string shortName) +{ + // Ensure their lengths are okay + name = truncateName(name); + shortName = truncateShortName(shortName); + + struct SRequest : HciAdapter::Header + { + char name[249]; + char shortName[11]; + } __attribute__((packed)); + + struct SResponse : HciAdapter::ResponseEvent + { + char name[249]; + char shortName[11]; + } __attribute__((packed)); + + SRequest request; + request.code = 0x000F; + request.controllerId = controllerIndex; + request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::Header); + + memset(request.name, 0, sizeof(request.name)); + snprintf(request.name, sizeof(request.name), "%s", name.c_str()); + + 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))) + { + 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; +} + +// Set a setting state to 'newState' +// +// 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) +{ + struct SRequest : HciAdapter::Header + { + 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.state = newState; + + SResponse response; + if (!hciAdapter.sendCommand(request, response, sizeof(response))) + { + Logger::warn(SSTR << " + Failed to set " << pSettingName << " state to: " << static_cast(newState)); + return false; + } + + response.toHost(); + + Logger::info(SSTR << " + " << pSettingName << " set to " << static_cast(newState) << ": " << controllerSettingsString(response.currentSettings)); + return true; +} + +// Set the powered state to `newState` (true = powered on, false = powered off) +// +// Returns true on success, otherwise false +bool Mgmt::setPowered(bool newState) +{ + return setState("Powered", 0x0005, controllerIndex, newState ? 1 : 0); +} + +// Set the BR/EDR state to `newState` (true = enabled, false = disabled) +// +// Returns true on success, otherwise false +bool Mgmt::setBredr(bool newState) +{ + return setState("BR/EDR", 0x002A, controllerIndex, newState ? 1 : 0); +} + +// Set the Secure Connection state (0 = disabled, 1 = enabled, 2 = secure connections only mode) +// +// Returns true on success, otherwise false +bool Mgmt::setSecureConnections(uint8_t newState) +{ + return setState("SecureConnections", 0x002D, controllerIndex, newState); +} + +// Set the bondable state to `newState` (true = enabled, false = disabled) +// +// Returns true on success, otherwise false +bool Mgmt::setBondable(bool newState) +{ + return setState("SecureConnections", 0x0009, controllerIndex, newState ? 1 : 0); +} + +// Set the connectable state to `newState` (true = enabled, false = disabled) +// +// Returns true on success, otherwise false +bool Mgmt::setConnectable(bool newState) +{ + return setState("Connectable", 0x0007, controllerIndex, newState ? 1 : 0); +} + +// Set the LE state to `newState` (true = enabled, false = disabled) +// +// Returns true on success, otherwise false +bool Mgmt::setLE(bool newState) +{ + return setState("LowEnergy", 0x000D, controllerIndex, newState ? 1 : 0); +} + +// Set the advertising state to `newState` (0 = disabled, 1 = enabled (with consideration towards the connectable setting), +// 2 = enabled in connectable mode). +// +// Returns true on success, otherwise false +bool Mgmt::setAdvertising(uint8_t newState) +{ + return setState("Advertising", 0x0029, 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) +{ + if (name.length() <= kMaxNameLength) + { + return name; + } + + return name.substr(0, kMaxNameLength); +} + +// Truncates the string `name` to the maximum allowed length for an adapter short-name. If `name` needs no truncation, a copy +// of `name` is returned. +std::string Mgmt::truncateShortName(const std::string &name) +{ + if (name.length() <= kMaxShortNameLength) + { + return name; + } + + return name.substr(0, kMaxShortNameLength); +} -- cgit v1.2.3