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/HciSocket.cpp | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/HciSocket.cpp (limited to 'src/HciSocket.cpp') diff --git a/src/HciSocket.cpp b/src/HciSocket.cpp new file mode 100644 index 0000000..09da6d6 --- /dev/null +++ b/src/HciSocket.cpp @@ -0,0 +1,198 @@ +// 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 +// >> +// +// Low-level socket interface used to communicate with the Bluetooth Management API (see HciAdapter.cpp) +// +// >> +// >>> DISCUSSION +// >> +// +// This class intended to support `HciAdapter` - see HciAdapter.cpp for more information. +// +// This file contains raw communication layer for the Bluetooth Management API, which is used to configure the Bluetooth adapter +// (such as enabling LE, setting the device name, etc.) This class is used by HciAdapter (HciAdapter.h) to perform higher-level +// functions. +// +// This code is for example purposes only. If you plan to use this in a production environment, I suggest rewriting it. +// +// The information for this implementation (as well as HciAdapter.h) came from: +// +// https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/mgmt-api.txt +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#include +#include +#include + +#include "HciSocket.h" +#include "Logger.h" +#include "Utils.h" + +// Initializes an unconnected socket +HciSocket::HciSocket() +: fdSocket(-1) +{ +} + +// Socket destructor +// +// This will automatically disconnect the socket if it is currently connected +HciSocket::~HciSocket() +{ + disconnect(); +} + +// Connects to an HCI socket using the Bluetooth Management API protocol +// +// Returns true on success, otherwise false +bool HciSocket::connect() +{ + disconnect(); + + fdSocket = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI); + if (fdSocket < 0) + { + logErrno("Connect(socket)"); + return false; + } + + struct sockaddr_hci addr; + memset(&addr, 0, sizeof(addr)); + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = HCI_DEV_NONE; + addr.hci_channel = HCI_CHANNEL_CONTROL; + + if (bind(fdSocket, reinterpret_cast(&addr), sizeof(addr)) < 0) + { + logErrno("Connect(bind)"); + disconnect(); + return false; + } + + Logger::debug(SSTR << "Connected to HCI control socket (file descriptor = " << fdSocket << ")"); + + return true; +} + +// Returns true if the socket is currently connected, otherwise false +bool HciSocket::isConnected() const +{ + return fdSocket >= 0; +} + +// Disconnects from the HCI socket +void HciSocket::disconnect() +{ + if (isConnected()) + { + close(fdSocket); + fdSocket = -1; + } +} + +// 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`. +// +// Returns true if any data was read successfully, otherwise false is returned in the case of an error or a timeout. +bool HciSocket::read(std::vector &response) const +{ + // Clear out our response + response.clear(); + + uint8_t responseChunk[kResponseChunkSize]; + + int retryTimeMS = 0; + while (retryTimeMS < kMaxRetryTimeMS && !ggkIsServerRunning()) + { + ssize_t bytesRead = ::read(fdSocket, responseChunk, kResponseChunkSize); + if (bytesRead > 0) + { + 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 insertBuf(responseChunk, responseChunk + bytesRead); + response.insert(response.end(), insertBuf.begin(), insertBuf.end()); + } + else + { + // If we have data, we're at the end + if (response.size() != 0) + { + break; + } + } + + // Retry (or continue reading) + std::this_thread::sleep_for(std::chrono::milliseconds(kRetryIntervalMS)); + retryTimeMS += kRetryIntervalMS; + } + + // Did we time out? + if (retryTimeMS >= kMaxRetryTimeMS) + { + logErrno("read(header)"); + return false; + } + + Logger::debug(SSTR << " + Read " << response.size() << " bytes"); + Logger::debug(SSTR << Utils::hex(response.data(), response.size())); + return true; +} + +// Writes the array of bytes of a given count +// +// This method returns true if the bytes were written successfully, otherwise false +bool HciSocket::write(std::vector buffer) const +{ + return write(buffer.data(), buffer.size()); +} + +// Writes the array of bytes of a given count +// +// 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)); + + size_t len = ::write(fdSocket, pBuffer, count); + + if (len != count) + { + logErrno("write"); + return false; + } + + return true; +} + +// 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)); +} -- cgit v1.2.3