diff options
-rw-r--r-- | api/direct_bt/DBTAdapter.hpp | 12 | ||||
-rw-r--r-- | api/direct_bt/DBTDevice.hpp | 27 | ||||
-rw-r--r-- | examples/direct_bt_scanner00/dbt_scanner00.cpp | 26 | ||||
-rw-r--r-- | examples/direct_bt_scanner01/dbt_scanner01.cpp | 20 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 363 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTAdapter.java | 9 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 20 | ||||
-rw-r--r-- | src/direct_bt/DBTAdapter.cpp | 20 | ||||
-rw-r--r-- | src/direct_bt/DBTDevice.cpp | 47 | ||||
-rw-r--r-- | src/direct_bt/HCIComm.cpp | 5 |
10 files changed, 472 insertions, 77 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp index ec66a74a..06c21de2 100644 --- a/api/direct_bt/DBTAdapter.hpp +++ b/api/direct_bt/DBTAdapter.hpp @@ -118,11 +118,11 @@ namespace direct_bt { std::shared_ptr<HCIComm> hciComm; std::vector<std::shared_ptr<DBTDevice>> connectedDevices; - std::recursive_mutex mtx_connectedDevices; - std::vector<std::shared_ptr<DBTDevice>> discoveredDevices; // all discovered devices std::vector<std::shared_ptr<DBTDevice>> sharedDevices; // all active shared devices std::vector<std::shared_ptr<AdapterStatusListener>> statusListenerList; + std::recursive_mutex mtx_hci; + std::recursive_mutex mtx_connectedDevices; std::recursive_mutex mtx_discoveredDevices; std::recursive_mutex mtx_sharedDevices; std::recursive_mutex mtx_statusListenerList; @@ -249,15 +249,15 @@ namespace direct_bt { DBTManager& getManager() const { return mgmt; } /** - * Returns a reference to the newly opened HCIComm instance - * if successful, otherwise nullptr is returned. + * Returns a reference to the already opened HCIComm + * or the newly opened HCIComm instance, otherwise nullptr if no success. */ std::shared_ptr<HCIComm> openHCI(); /** - * Returns the {@link #openHCI()} session or {@code nullptr} if closed. + * Returns the {@link #openHCI()} HCIComm or {@code nullptr} if closed. */ - std::shared_ptr<HCIComm> getOpenHCIComm() const { return hciComm; } + std::shared_ptr<HCIComm> getHCI() const; /** * Closes the HCIComm instance diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp index 9edf1c6d..6271b982 100644 --- a/api/direct_bt/DBTDevice.hpp +++ b/api/direct_bt/DBTDevice.hpp @@ -65,6 +65,7 @@ namespace direct_bt { std::shared_ptr<ManufactureSpecificData> msd = nullptr; std::vector<std::shared_ptr<uuid_t>> services; std::shared_ptr<GATTHandler> gattHandler = nullptr; + std::recursive_mutex mtx_data; std::recursive_mutex mtx_gatt; DBTDevice(DBTAdapter & adapter, EInfoReport const & r); @@ -78,6 +79,7 @@ namespace direct_bt { std::shared_ptr<DBTDevice> getSharedInstance() const; void releaseSharedInstance() const; + void notifyDisconnected(); public: const uint64_t ts_creation; @@ -110,14 +112,12 @@ namespace direct_bt { bool isLEAddressType() const { return BDADDR_LE_PUBLIC == addressType || BDADDR_LE_RANDOM == addressType; } bool isBREDRAddressType() const { return BDADDR_BREDR == addressType; } - std::string const & getName() const { return name; } - bool hasName() const { return name.length()>0; } int8_t getRSSI() const { return rssi; } int8_t getTxPower() const { return tx_power; } uint16_t getAppearance() const { return appearance; } - std::shared_ptr<ManufactureSpecificData> const getManufactureSpecificData() const { return msd; } - - std::vector<std::shared_ptr<uuid_t>> getServices() const { return services; } + std::string const getName() const; + std::shared_ptr<ManufactureSpecificData> const getManufactureSpecificData() const; + std::vector<std::shared_ptr<uuid_t>> getServices() const; /** Returns index >= 0 if found, otherwise -1 */ int findService(std::shared_ptr<uuid_t> const &uuid) const; @@ -143,7 +143,7 @@ namespace direct_bt { * Returns the new connection handle or 0 if not successful. * </p> * <p> - * The device is tracked by the managing adapter's HCISession instance. + * The device is tracked by the managing adapter. * </p> * <p> * Default parameter values are chosen for using public address resolution @@ -167,7 +167,7 @@ namespace direct_bt { * Returns the new connection handle or 0 if not successful. * </p> * <p> - * The device is tracked by the managing adapter's HCISession instance. + * The device is tracked by the managing adapter. * </p> */ uint16_t connectHCI(const uint16_t pkt_type=HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5, @@ -183,7 +183,7 @@ namespace direct_bt { * Returns the new connection handle or 0 if not successful. * </p> * <p> - * The device is tracked by the managing adapter's HCISession instance. + * The device is tracked by the managing adapter. * </p> */ uint16_t connectHCIDefault(); @@ -195,7 +195,7 @@ namespace direct_bt { /** * Disconnect the LE or BREDR peer's GATT and HCI connection. * <p> - * The device will be removed from the managing adapter's HCISession instance. + * The device will be removed from the managing adapter's connected devices. * </p> * <p> * An open GATTHandler will also be closed via disconnectGATT() @@ -220,8 +220,8 @@ namespace direct_bt { /** * Returns a newly established GATT connection or an already open GATT connection. * <p> - * The HCI le_connect or HCI connect (defaultConnect) must be performed first, - * to produce orderly behavior and best performance. + * The HCI le_connect or HCI connect shall be performed first, + * to produce best performance. See {@link #connectHCIDefault()}. * </p> * <p> * The returned GATTHandler is managed by this device instance @@ -238,13 +238,10 @@ namespace direct_bt { * Returns a list of shared GATTServices available on this device if successful, * otherwise returns an empty list. * <p> - * In case no HCI connection has been established yet, connectHCIDefault() will be performed. - * </p> - * <p> * In case no GATT connection has been established yet, connectGATT(..) will be performed. * </p> */ - std::vector<std::shared_ptr<GATTService>> getServices(); + std::vector<std::shared_ptr<GATTService>> getGATTServices(); /** * Explicit disconnecting an open GATTHandler, which is usually performed via disconnect() diff --git a/examples/direct_bt_scanner00/dbt_scanner00.cpp b/examples/direct_bt_scanner00/dbt_scanner00.cpp index ba7ba732..966f16d5 100644 --- a/examples/direct_bt_scanner00/dbt_scanner00.cpp +++ b/examples/direct_bt_scanner00/dbt_scanner00.cpp @@ -185,10 +185,12 @@ int main(int argc, char *argv[]) const int64_t t0 = getCurrentMilliseconds(); - std::shared_ptr<HCIComm> hci = adapter.openHCI(); - if( nullptr == hci || !hci->isOpen() ) { - fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str()); - exit(1); + if( doHCI_Connect ) { + std::shared_ptr<HCIComm> hci = adapter.openHCI(); + if( nullptr == hci || !hci->isOpen() ) { + fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str()); + exit(1); + } } while( ok && ( forever || !foundDevice ) ) { @@ -218,9 +220,23 @@ int main(int argc, char *argv[]) const uint64_t t1 = getCurrentMilliseconds(); // + // HCI LE-Connect + // (Without: Overall communication takes ~twice as long!!!) + // + if( doHCI_Connect ) { + if( 0 == device->connectHCIDefault() ) { + fprintf(stderr, "Connect: Failed %s\n", device->toString().c_str()); + } else { + fprintf(stderr, "Connect: Success\n"); + } + } else { + fprintf(stderr, "Connect: Skipped %s\n", device->toString().c_str()); + } + + // // GATT Service Processing // - std::vector<GATTServiceRef> primServices = device->getServices(); // implicit HCI and GATT connect... + std::vector<GATTServiceRef> primServices = device->getGATTServices(); // implicit GATT connect... if( primServices.size() > 0 ) { const uint64_t t5 = getCurrentMilliseconds(); { diff --git a/examples/direct_bt_scanner01/dbt_scanner01.cpp b/examples/direct_bt_scanner01/dbt_scanner01.cpp index 9844ee30..7eb50d62 100644 --- a/examples/direct_bt_scanner01/dbt_scanner01.cpp +++ b/examples/direct_bt_scanner01/dbt_scanner01.cpp @@ -183,10 +183,12 @@ int main(int argc, char *argv[]) const int64_t t0 = getCurrentMilliseconds(); - std::shared_ptr<HCIComm> hci = adapter.openHCI(); - if( nullptr == hci || !hci->isOpen() ) { - fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str()); - exit(1); + if( doHCI_Connect ) { + std::shared_ptr<HCIComm> hci = adapter.openHCI(); + if( nullptr == hci || !hci->isOpen() ) { + fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str()); + exit(1); + } } while( ok && ( forever || !foundDevice ) ) { @@ -219,17 +221,14 @@ int main(int argc, char *argv[]) // HCI LE-Connect // (Without: Overall communication takes ~twice as long!!!) // - uint16_t hciConnHandle; if( doHCI_Connect ) { - hciConnHandle = device->connectHCIDefault(); - if( 0 == hciConnHandle ) { + if( 0 == device->connectHCIDefault() ) { fprintf(stderr, "Connect: Failed %s\n", device->toString().c_str()); } else { fprintf(stderr, "Connect: Success\n"); } } else { fprintf(stderr, "Connect: Skipped %s\n", device->toString().c_str()); - hciConnHandle = 0; } const uint64_t t3 = getCurrentMilliseconds(); const uint64_t td03 = t3 - t0; @@ -238,9 +237,8 @@ int main(int argc, char *argv[]) fprintf(stderr, " discovery-only %" PRIu64 " ms,\n" " connect-only %" PRIu64 " ms,\n" " discovered to hci-connected %" PRIu64 " ms,\n" - " total %" PRIu64 " ms,\n" - " handle 0x%X\n", - td01, td13, (t3 - device->getCreationTimestamp()), td03, hciConnHandle); + " total %" PRIu64 " ms,\n", + td01, td13, (t3 - device->getCreationTimestamp()), td03); // // GATT Processing diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp new file mode 100644 index 00000000..b5a7ebb8 --- /dev/null +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -0,0 +1,363 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <direct_bt/DirectBT.hpp> +#include <cinttypes> + +extern "C" { + #include <unistd.h> +} + +using namespace direct_bt; + +/*** + * This C++ direct_bt scanner code uses the high-level API like dbt_scanner00 + * and adds multithreading, i.e. one thread processes each found device found + * as notified via the event listener. + */ + +static int64_t timestamp_t0; + +static EUI48 waitForDevice = EUI48_ANY_DEVICE; + +static void deviceConnectTask(std::shared_ptr<DBTDevice> device); + +static void deviceProcessTask(std::shared_ptr<DBTDevice> device); + +#include <pthread.h> + +class DeviceTask { + public: + std::shared_ptr<DBTDevice> device; + std::thread worker; + + DeviceTask(std::shared_ptr<DBTDevice> d) + : device(d), + worker( std::thread(::deviceProcessTask, d) ) + { + worker.detach(); + fprintf(stderr, "DeviceTask ctor: %s\n", d->toString().c_str()); + } + + DeviceTask(const DeviceTask &o) noexcept = default; + DeviceTask(DeviceTask &&o) noexcept = default; + DeviceTask& operator=(const DeviceTask &o) noexcept = default; + DeviceTask& operator=(DeviceTask &&o) noexcept = default; + + ~DeviceTask() { + fprintf(stderr, "DeviceTask dtor: %s\n", device->toString().c_str()); + } + +}; + +/** + * TODO: Analyze why 'vector<DeviceTask>' regarding field 'std::thread' + * does _not_ work. Instance vector of DeviceTask with std::thread + * causes multiple occasions of SIGSEGFAULT and mutex issues! + */ +static std::vector<std::shared_ptr<DeviceTask>> deviceTasks; +static std::recursive_mutex mtx_deviceTasks; + +static int getDeviceTaskCount() { + const std::lock_guard<std::recursive_mutex> lock(mtx_deviceTasks); // RAII-style acquire and relinquish via destructor + return deviceTasks.size(); +} +static bool isDeviceTaskInProgress(std::shared_ptr<DBTDevice> d) { + const std::lock_guard<std::recursive_mutex> lock(mtx_deviceTasks); // RAII-style acquire and relinquish via destructor + for (auto it = deviceTasks.begin(); it != deviceTasks.end(); ++it) { + if ( *d == *(*it)->device ) { + return true; + } + } + return false; +} +static bool addDeviceTask(std::shared_ptr<DBTDevice> d) { + const std::lock_guard<std::recursive_mutex> lock(mtx_deviceTasks); // RAII-style acquire and relinquish via destructor + if( !isDeviceTaskInProgress(d) ) { + deviceTasks.push_back( std::shared_ptr<DeviceTask>( new DeviceTask(d) ) ); + return true; + } + return false; +} +static bool removeDeviceTask(std::shared_ptr<DBTDevice> d) { + const std::lock_guard<std::recursive_mutex> lock(mtx_deviceTasks); // RAII-style acquire and relinquish via destructor + for (auto it = deviceTasks.begin(); it != deviceTasks.end(); ) { + if ( *d == *(*it)->device ) { + it = deviceTasks.erase(it); + return true; + } else { + ++it; + } + } + return false; +} + +static std::recursive_mutex mtx_devicesProcessed; +static std::vector<EUI48> devicesProcessed; + +static void addDevicesProcessed(const EUI48 &a) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + devicesProcessed.push_back(a); +} +static bool isDeviceProcessed(const EUI48 & a) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + for (auto it = devicesProcessed.begin(); it != devicesProcessed.end(); ++it) { + if ( a == *it ) { + return true; + } + } + return false; +} + +class MyAdapterStatusListener : public AdapterStatusListener { + + void adapterSettingsChanged(DBTAdapter const &a, const AdapterSetting oldmask, const AdapterSetting newmask, + const AdapterSetting changedmask, const uint64_t timestamp) override { + fprintf(stderr, "****** SETTINGS_CHANGED: %s -> %s, changed %s\n", + adapterSettingsToString(oldmask).c_str(), + adapterSettingsToString(newmask).c_str(), + adapterSettingsToString(changedmask).c_str()); + fprintf(stderr, "Status DBTAdapter:\n"); + fprintf(stderr, "%s\n", a.toString().c_str()); + (void)timestamp; + } + + void deviceFound(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override { + (void)timestamp; + + if( waitForDevice == EUI48_ANY_DEVICE || + ( waitForDevice == device->address && !isDeviceProcessed(waitForDevice) ) ) + { + fprintf(stderr, "****** FOUND__-0: Connecting %s\n", device->toString().c_str()); + std::thread dc(::deviceConnectTask, device); + dc.detach(); + } else { + fprintf(stderr, "****** FOUND__-1: NOP %s\n", device->toString().c_str()); + } + } + void deviceUpdated(std::shared_ptr<DBTDevice> device, const uint64_t timestamp, const EIRDataType updateMask) override { + fprintf(stderr, "****** UPDATED: %s of %s\n", eirDataMaskToString(updateMask).c_str(), device->toString().c_str()); + (void)timestamp; + } + void deviceConnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override { + (void)timestamp; + + if( waitForDevice == EUI48_ANY_DEVICE || + ( waitForDevice == device->address && !isDeviceProcessed(waitForDevice) ) ) + { + fprintf(stderr, "****** CONNECTED-0: Processing %s\n", device->toString().c_str()); + addDeviceTask( device ); + } else { + fprintf(stderr, "****** CONNECTED-1: NOP %s\n", device->toString().c_str()); + } + } + void deviceDisconnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override { + fprintf(stderr, "****** DISCONNECTED: %s\n", device->toString().c_str()); + (void)timestamp; + } +}; + +static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT); + +class MyGATTEventListener : public SpecificGATTCharacteristicListener { + public: + + MyGATTEventListener(const GATTCharacteristic * characteristicMatch) + : SpecificGATTCharacteristicListener(characteristicMatch) {} + + void notificationReceived(GATTCharacteristicRef charDecl, std::shared_ptr<TROOctets> charValue, const uint64_t timestamp) override { + const std::shared_ptr<DBTDevice> dev = charDecl->getDevice(); + const int64_t tR = getCurrentMilliseconds(); + fprintf(stderr, "****** GATT Notify (td %" PRIu64 " ms, dev-discovered %" PRIu64 " ms): From %s\n", + (tR-timestamp), (tR-dev->ts_creation), dev->toString().c_str()); + if( nullptr != charDecl ) { + fprintf(stderr, "****** decl %s\n", charDecl->toString().c_str()); + } + fprintf(stderr, "****** rawv %s\n", charValue->toString().c_str()); + } + + void indicationReceived(GATTCharacteristicRef charDecl, + std::shared_ptr<TROOctets> charValue, const uint64_t timestamp, + const bool confirmationSent) override + { + const std::shared_ptr<DBTDevice> dev = charDecl->getDevice(); + const int64_t tR = getCurrentMilliseconds(); + fprintf(stderr, "****** GATT Indication (confirmed %d, td(msg %" PRIu64 " ms, dev-discovered %" PRIu64 " ms): From %s\n", + confirmationSent, (tR-timestamp), (tR-dev->ts_creation), dev->toString().c_str()); + if( nullptr != charDecl ) { + fprintf(stderr, "****** decl %s\n", charDecl->toString().c_str()); + if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) { + std::shared_ptr<TemperatureMeasurementCharateristic> temp = TemperatureMeasurementCharateristic::get(*charValue); + if( nullptr != temp ) { + fprintf(stderr, "****** valu %s\n", temp->toString().c_str()); + } + } + } + fprintf(stderr, "****** rawv %s\n", charValue->toString().c_str()); + } +}; + +static void deviceConnectTask(std::shared_ptr<DBTDevice> device) { + fprintf(stderr, "****** Device Connector: Start %s\n", device->toString().c_str()); + device->getAdapter().stopDiscovery(); + bool res = device->connectHCIDefault(); + fprintf(stderr, "****** Device Connector: End result %d of %s\n", res, device->toString().c_str()); + if( !res && 0 == getDeviceTaskCount() ) { + fprintf(stderr, "****** Device Connector: startDiscovery()\n"); + device->getAdapter().startDiscovery(); + } +} + +static void deviceProcessTask(std::shared_ptr<DBTDevice> device) { + fprintf(stderr, "****** Device Process: Start %s\n", device->toString().c_str()); + const uint64_t t1 = getCurrentMilliseconds(); + + // + // GATT Service Processing + // + std::vector<GATTServiceRef> primServices; + std::shared_ptr<GATTHandler> gatt = device->connectGATT(); + if( nullptr == gatt ) { + fprintf(stderr, "****** Device Process: GATT Connect failed\n"); + goto out; + } + primServices = device->getGATTServices(); // implicit GATT connect... + if( primServices.size() > 0 ) { + const uint64_t t5 = getCurrentMilliseconds(); + { + const uint64_t td15 = t5 - t1; // connected -> gatt-complete + const uint64_t tdc5 = t5 - device->getCreationTimestamp(); // discovered to gatt-complete + const uint64_t td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete + fprintf(stderr, "\n\n\n"); + fprintf(stderr, "GATT primary-services completed\n"); + fprintf(stderr, " connected to gatt-complete %" PRIu64 " ms,\n" + " discovered to gatt-complete %" PRIu64 " ms (connect %" PRIu64 " ms),\n" + " adapter-init to gatt-complete %" PRIu64 " ms\n\n", + td15, tdc5, (tdc5 - td15), td05); + } + + for(size_t i=0; i<primServices.size(); i++) { + GATTService & primService = *primServices.at(i); + fprintf(stderr, " [%2.2d] Service %s\n", (int)i, primService.toString().c_str()); + fprintf(stderr, " [%2.2d] Service Characteristics\n", (int)i); + std::vector<GATTCharacteristicRef> & serviceCharacteristics = primService.characteristicList; + for(size_t j=0; j<serviceCharacteristics.size(); j++) { + GATTCharacteristic & serviceChar = *serviceCharacteristics.at(j); + fprintf(stderr, " [%2.2d.%2.2d] Decla: %s\n", (int)i, (int)j, serviceChar.toString().c_str()); + if( serviceChar.hasProperties(GATTCharacteristic::PropertyBitVal::Read) ) { + POctets value(GATTHandler::ClientMaxMTU, 0); + if( serviceChar.readValue(value) ) { + fprintf(stderr, " [%2.2d.%2.2d] Value: %s\n", (int)i, (int)j, value.toString().c_str()); + } + } + bool cccdEnableResult[2]; + bool cccdRet = serviceChar.configIndicationNotification(true /* enableNotification */, true /* enableIndication */, cccdEnableResult); + fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n", + (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], cccdRet); + if( cccdRet ) { + serviceChar.addCharacteristicListener( std::shared_ptr<GATTCharacteristicListener>( new MyGATTEventListener(&serviceChar) ) ); + } + } + } + // FIXME sleep 1s for potential callbacks .. + sleep(1); + } + device->disconnect(); + +out: + addDevicesProcessed(device->getAddress()); + if( 1 >= getDeviceTaskCount() ) { + fprintf(stderr, "****** Device Process: startDiscovery()\n"); + device->getAdapter().startDiscovery(); + } + removeDeviceTask(device); + fprintf(stderr, "****** Device Process: End\n"); +} + + +int main(int argc, char *argv[]) +{ + int dev_id = 0; // default + bool waitForEnter=false; + bool done = false; + + for(int i=1; i<argc; i++) { + if( !strcmp("-wait", argv[i]) ) { + waitForEnter = true; + } else if( !strcmp("-dev_id", argv[i]) && argc > (i+1) ) { + dev_id = atoi(argv[++i]); + } else if( !strcmp("-mac", argv[i]) && argc > (i+1) ) { + std::string macstr = std::string(argv[++i]); + waitForDevice = EUI48(macstr); + } + } + fprintf(stderr, "dev_id %d\n", dev_id); + fprintf(stderr, "waitForDevice: %s\n", waitForDevice.toString().c_str()); + + if( waitForEnter ) { + fprintf(stderr, "Press ENTER to continue\n"); + getchar(); + } + + timestamp_t0 = getCurrentMilliseconds(); + + DBTAdapter adapter(dev_id); + if( !adapter.hasDevId() ) { + fprintf(stderr, "Default adapter not available.\n"); + exit(1); + } + if( !adapter.isValid() ) { + fprintf(stderr, "Adapter invalid.\n"); + exit(1); + } + fprintf(stderr, "Using adapter: device %s, address %s: %s\n", + adapter.getName().c_str(), adapter.getAddressString().c_str(), adapter.toString().c_str()); + + adapter.addStatusListener(std::shared_ptr<AdapterStatusListener>(new MyAdapterStatusListener())); + + std::shared_ptr<HCIComm> hci = adapter.openHCI(); + if( nullptr == hci || !hci->isOpen() ) { + fprintf(stderr, "Couldn't open HCI from %s\n", adapter.toString().c_str()); + exit(1); + } + + if( !adapter.startDiscovery() ) { + perror("Adapter start discovery failed"); + goto out; + } + + do { + if( waitForDevice != EUI48_ANY_DEVICE && isDeviceProcessed(waitForDevice) ) { + fprintf(stderr, "****** WaitForDevice processed %s", waitForDevice.toString().c_str()); + done = true; + } + sleep(3); + } while( !done ); + +out: + adapter.closeHCI(); + return 0; +} + diff --git a/java/direct_bt/tinyb/DBTAdapter.java b/java/direct_bt/tinyb/DBTAdapter.java index 6c9e9f03..9b60d27b 100644 --- a/java/direct_bt/tinyb/DBTAdapter.java +++ b/java/direct_bt/tinyb/DBTAdapter.java @@ -233,13 +233,6 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter /* internal */ - private synchronized void open() { - if( !isOpen ) { - isOpen = openImpl(); - } - } - private native boolean openImpl(); - @Override protected native void deleteImpl(); @@ -247,7 +240,6 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter @Override public synchronized boolean startDiscovery() throws BluetoothException { - open(); removeDevices(); final boolean res = startDiscoveryImpl(); isDiscovering = res; @@ -278,7 +270,6 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter @Override public int removeDevices() throws BluetoothException { - open(); final int cj = removeDiscoveredDevices(); final int cn = removeDevicesImpl(); if( cj != cn ) { diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 360084b1..1e32ade8 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -392,21 +392,6 @@ jstring Java_direct_1bt_tinyb_DBTAdapter_toStringImpl(JNIEnv *env, jobject obj) return nullptr; } -jboolean Java_direct_1bt_tinyb_DBTAdapter_openImpl(JNIEnv *env, jobject obj) { - try { - DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); - DBG_PRINT("Java_direct_1bt_tinyb_DBTAdapter_deleteImpl %s", adapter->toString().c_str()); - std::shared_ptr<direct_bt::HCIComm> hciSession = adapter->openHCI(); - if( nullptr == hciSession ) { - throw BluetoothException("Couldn't open adapter "+adapter->toString(), E_FILE_LINE); - } - return JNI_TRUE; - } catch(...) { - rethrow_and_raise_java_exception(env); - } - return JNI_FALSE; -} - void Java_direct_1bt_tinyb_DBTAdapter_deleteImpl(JNIEnv *env, jobject obj) { try { @@ -594,6 +579,11 @@ jobject Java_direct_1bt_tinyb_DBTAdapter_connectDevice(JNIEnv *env, jobject obj, getBDAddressTypeString(addressType).c_str(), getBDAddressTypeString(addressTypeDevice).c_str(), device->toString().c_str()); } + + std::shared_ptr<direct_bt::HCIComm> hci = adapter->openHCI(); + if( nullptr == hci ) { + throw BluetoothException("Couldn't get or open adapter's HCIComm "+adapter->toString(), E_FILE_LINE); + } std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject(); JavaGlobalObj::check(jDeviceRef, E_FILE_LINE); diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp index ec6775ee..0ff9a3b6 100644 --- a/src/direct_bt/DBTAdapter.cpp +++ b/src/direct_bt/DBTAdapter.cpp @@ -55,7 +55,7 @@ void DBTAdapter::addConnectedDevice(const std::shared_ptr<DBTDevice> & device) { const std::lock_guard<std::recursive_mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor for (auto it = connectedDevices.begin(); it != connectedDevices.end(); ++it) { if ( *device == **it ) { - ERR_PRINT("DBTAdapter::addConnectedDevice: Device already connected: %s", device->toString().c_str()); + DBG_PRINT("DBTAdapter::addConnectedDevice: Device already connected: %s", device->toString().c_str()); return; } } @@ -175,9 +175,18 @@ void DBTAdapter::setBondable(bool value) { std::shared_ptr<HCIComm> DBTAdapter::openHCI() { + const std::lock_guard<std::recursive_mutex> lock(mtx_hci); // RAII-style acquire and relinquish via destructor + if( !valid ) { return nullptr; } + if( nullptr != hciComm ) { + if( hciComm->isOpen() ) { + DBG_PRINT("DBTAdapter::openHCI: Already open"); + return hciComm; + } + hciComm = nullptr; + } HCIComm *s = new HCIComm(dev_id, HCI_CHANNEL_RAW, HCIDefaults::HCI_TO_SEND_REQ_POLL_MS); if( !s->isOpen() ) { delete s; @@ -188,8 +197,15 @@ std::shared_ptr<HCIComm> DBTAdapter::openHCI() return hciComm; } +std::shared_ptr<HCIComm> DBTAdapter::getHCI() const { + const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTAdapter*>(this)->mtx_hci); // RAII-style acquire and relinquish via destructor + return hciComm; +} + bool DBTAdapter::closeHCI() { + const std::lock_guard<std::recursive_mutex> lock(mtx_hci); // RAII-style acquire and relinquish via destructor + DBG_PRINT("DBTAdapter::closeHCI: ..."); if( nullptr == hciComm || !hciComm->isOpen() ) { DBG_PRINT("DBTAdapter::closeHCI: Not open"); @@ -500,6 +516,8 @@ bool DBTAdapter::mgmtEvDeviceDisconnectedCB(std::shared_ptr<MgmtEvent> e) { if( nullptr != device ) { DBG_PRINT("DBTAdapter::EventCB:DeviceDisconnected(dev_id %d): %s\n -> %s", dev_id, event.toString().c_str(), device->toString().c_str()); + device->notifyDisconnected(); + removeConnectedDevice(*device); for_each_idx_mtx(mtx_statusListenerList, statusListenerList, [&](std::shared_ptr<AdapterStatusListener> &l) { if( l->matchDevice(*device) ) { diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp index 75ac8f09..50c9d170 100644 --- a/src/direct_bt/DBTDevice.cpp +++ b/src/direct_bt/DBTDevice.cpp @@ -99,10 +99,26 @@ int DBTDevice::findService(std::shared_ptr<uuid_t> const &uuid) const } } +std::string const DBTDevice::getName() const { + const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTDevice*>(this)->mtx_data); // RAII-style acquire and relinquish via destructor + return name; +} + +std::shared_ptr<ManufactureSpecificData> const DBTDevice::getManufactureSpecificData() const { + const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTDevice*>(this)->mtx_data); // RAII-style acquire and relinquish via destructor + return msd; +} + +std::vector<std::shared_ptr<uuid_t>> DBTDevice::getServices() const { + const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTDevice*>(this)->mtx_data); // RAII-style acquire and relinquish via destructor + return services; +} + std::string DBTDevice::toString() const { + const std::lock_guard<std::recursive_mutex> lock(const_cast<DBTDevice*>(this)->mtx_data); // RAII-style acquire and relinquish via destructor const uint64_t t0 = getCurrentMilliseconds(); std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]"; - std::string out("Device[address["+getAddressString()+", "+getBDAddressTypeString(getAddressType())+"], name['"+getName()+ + std::string out("Device[address["+getAddressString()+", "+getBDAddressTypeString(getAddressType())+"], name['"+name+ "'], age "+std::to_string(t0-ts_creation)+" ms, lup "+std::to_string(t0-ts_update)+" ms, rssi "+std::to_string(getRSSI())+ ", tx-power "+std::to_string(tx_power)+", appearance "+uint16HexString(appearance)+", "+msdstr+", "+javaObjectToString()+"]"); if(services.size() > 0 ) { @@ -120,6 +136,8 @@ std::string DBTDevice::toString() const { } EIRDataType DBTDevice::update(EInfoReport const & data) { + const std::lock_guard<std::recursive_mutex> lock(mtx_data); // RAII-style acquire and relinquish via destructor + EIRDataType res = EIRDataType::NONE; ts_update = data.getTimestamp(); if( data.isSet(EIRDataType::BDADDR) ) { @@ -212,13 +230,15 @@ uint16_t DBTDevice::le_connectHCI(HCIAddressType peer_mac_type, HCIAddressType o ERR_PRINT("DBTDevice::le_connect: Already connected"); return 0; } + std::shared_ptr<DBTDevice> sharedInstance = getSharedInstance(); if( nullptr == sharedInstance ) { throw InternalError("DBTDevice::connectGATT: Device unknown to adapter and not tracked: "+toString(), E_FILE_LINE); } - std::shared_ptr<HCIComm> hciComm = adapter.getOpenHCIComm(); + const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor + std::shared_ptr<HCIComm> hciComm = adapter.getHCI(); if( nullptr == hciComm || !hciComm->isOpen() ) { - ERR_PRINT("DBTDevice::le_connect: Adapter session not opened"); + ERR_PRINT("DBTDevice::le_connect: Adapter's HCIComm not opened"); return 0; } if( !isLEAddressType() ) { @@ -256,9 +276,10 @@ uint16_t DBTDevice::connectHCI(const uint16_t pkt_type, const uint16_t clock_off if( nullptr == sharedInstance ) { throw InternalError("DBTDevice::connectGATT: Device unknown to adapter and not tracked: "+toString(), E_FILE_LINE); } - std::shared_ptr<HCIComm> hciComm = adapter.getOpenHCIComm(); + const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor + std::shared_ptr<HCIComm> hciComm = adapter.getHCI(); if( nullptr == hciComm || !hciComm->isOpen() ) { - ERR_PRINT("DBTDevice::le_connect: Adapter session not opened"); + ERR_PRINT("DBTDevice::le_connect: Adapter's HCIComm not opened"); return 0; } if( !isBREDRAddressType() ) { @@ -298,10 +319,15 @@ uint16_t DBTDevice::connectHCIDefault() } } +void DBTDevice::notifyDisconnected() { + hciConnHandle = 0; +} + void DBTDevice::disconnect(const uint8_t reason) { disconnectGATT(); - std::shared_ptr<HCIComm> hciComm = adapter.getOpenHCIComm(); + const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor + std::shared_ptr<HCIComm> hciComm = adapter.getHCI(); if( 0 == hciConnHandle ) { DBG_PRINT("DBTDevice::disconnect: Not connected"); @@ -309,7 +335,7 @@ void DBTDevice::disconnect(const uint8_t reason) { } if( nullptr == hciComm || !hciComm->isOpen() ) { - DBG_PRINT("DBTDevice::disconnect: Session not opened"); + DBG_PRINT("DBTDevice::disconnect: Adapter's HCIComm not opened"); goto errout; } @@ -361,13 +387,8 @@ std::shared_ptr<GATTHandler> DBTDevice::getGATTHandler() { return gattHandler; } -std::vector<std::shared_ptr<GATTService>> DBTDevice::getServices() { +std::vector<std::shared_ptr<GATTService>> DBTDevice::getGATTServices() { const std::lock_guard<std::recursive_mutex> lock(mtx_gatt); // RAII-style acquire and relinquish via destructor - std::shared_ptr<DBTDevice> sharedInstance = getSharedInstance(); - if( nullptr == sharedInstance ) { - ERR_PRINT("DBTDevice::getServices: Device unknown to adapter and not tracked: %s", toString().c_str()); - return std::vector<std::shared_ptr<GATTService>>(); - } if( nullptr == gattHandler ) { connectGATT(); if( nullptr == gattHandler ) { diff --git a/src/direct_bt/HCIComm.cpp b/src/direct_bt/HCIComm.cpp index 4945e4d8..1a89d853 100644 --- a/src/direct_bt/HCIComm.cpp +++ b/src/direct_bt/HCIComm.cpp @@ -32,7 +32,7 @@ #include <algorithm> -#define VERBOSE_ON 1 +// #define VERBOSE_ON 1 #include <dbt_debug.hpp> #include "HCIComm.hpp" @@ -64,7 +64,7 @@ int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel) } */ // Create a loose HCI socket - dd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); + dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); if (0 > dd ) { ERR_PRINT("HCIComm::hci_open_dev: socket failed"); return dd; @@ -341,6 +341,7 @@ bool HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t if (exp_event != HCI_EV_CMD_STATUS) { if (cs->status) { errno = EIO; + ERR_PRINT("hci_send_req: event exp 0x%X != has 0x%X, error status 0x%2.2X", exp_event, hdr->evt, cs->status); goto failed; } break; |