diff options
author | Sven Gothel <[email protected]> | 2021-09-24 12:59:13 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-09-24 12:59:13 +0200 |
commit | 61bfb0757a1c337eaf86f9df8b0524bec1b7bc0f (patch) | |
tree | 55e81117467e342ba7c6ef7d7b6beb3a43b01655 | |
parent | 61108682ad95d12f66cc5ee6cca89a5bbf99c5fa (diff) |
HCIHandler/BTAdapter/BTDevice: Add le_set[_default]_phy(..) / set[Connected|Default]LE_PHY(..)
Note that the HCIHandler methods validate if LE_Features if host do support LE_2M.
le_set*_phy(): Missing LE_2M support causes command to fail early w/o issuing it.
le_read_phy(): Missing LE_2M support causes command to return LE_1M here.
+++
Also remove 'TODO' re scanning PHY, since we shall simply stick with LE_1M band for compatibility.
Connection itself may use the fast LE_2M or long range LE_CODED band.
-rw-r--r-- | api/direct_bt/BTAdapter.hpp | 20 | ||||
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 53 | ||||
-rw-r--r-- | api/direct_bt/HCIHandler.hpp | 39 | ||||
-rw-r--r-- | api/direct_bt/HCITypes.hpp | 16 | ||||
-rw-r--r-- | api/direct_bt/MgmtTypes.hpp | 42 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 14 | ||||
-rw-r--r-- | src/direct_bt/BTAdapter.cpp | 28 | ||||
-rw-r--r-- | src/direct_bt/BTDevice.cpp | 39 | ||||
-rw-r--r-- | src/direct_bt/HCIHandler.cpp | 127 | ||||
-rw-r--r-- | src/direct_bt/HCITypes.cpp | 1 |
10 files changed, 363 insertions, 16 deletions
diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index aedf3b8e..4d4f951d 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -391,6 +391,7 @@ namespace direct_bt { bool mgmtEvHCIEncryptionChangedHCI(const MgmtEvent& e) noexcept; bool mgmtEvHCIEncryptionKeyRefreshCompleteHCI(const MgmtEvent& e) noexcept; bool mgmtEvHCILERemoteUserFeaturesHCI(const MgmtEvent& e) noexcept; + bool mgmtEvHCILEPhyUpdateCompleteHCI(const MgmtEvent& e) noexcept; bool mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept; @@ -643,6 +644,25 @@ namespace direct_bt { HCIStatusCode reset() noexcept; /** + * Sets default preference of LE_PHYs. + * + * BT Core Spec v5.2: Vol 4, Part E, 7.8.49 LE Set PHY command + * + * @param tryTx if true, host has preference for given Tx LE_PHYs + * @param tryRx if true, host has preference for given Rx LE_PHYs + * @param Tx transmitter LE_PHYs of preference if tryTx is true, otherwise ignored + * @param Rx receiver LE_PHYs of preference if tryRx is true, otherwise ignored + * @return + * @see BTDevice::getTxPhys() + * @see BTDevice::getRxPhys() + * @see BTDevice::getConnectedLE_PHY() + * @see BTDevice::setConnectedLE_PHY() + * @since 2.4.0 + */ + HCIStatusCode setDefaultLE_PHY(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept; + + /** * Returns a reference to the used singleton BTManager instance, used to create this adapter. */ BTManager& getManager() const noexcept { return mgmt; } diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index a2d2c877..066ce5be 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -87,6 +87,8 @@ namespace direct_bt { AppearanceCat appearance = AppearanceCat::UNKNOWN; jau::relaxed_atomic_uint16 hciConnHandle; jau::ordered_atomic<LE_Features, std::memory_order_relaxed> le_features; + jau::ordered_atomic<LE_PHYs, std::memory_order_relaxed> le_phy_tx; + jau::ordered_atomic<LE_PHYs, std::memory_order_relaxed> le_phy_rx; std::shared_ptr<ManufactureSpecificData> advMSD = nullptr; jau::darray<std::shared_ptr<const jau::uuid_t>> advServices; #if SMP_SUPPORTED_BY_OS @@ -159,6 +161,7 @@ namespace direct_bt { void notifyDisconnected() noexcept; void notifyConnected(std::shared_ptr<BTDevice> sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept; void notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LE_Features features) noexcept; + void notifyLEPhyUpdateComplete(const LE_PHYs Tx, const LE_PHYs Rx) noexcept; /** * Setup L2CAP channel connection to device incl. optional security encryption level off-thread. @@ -496,11 +499,61 @@ namespace direct_bt { * @param resTx reference for the resulting transmitter LE_PHYs bit * @param resRx reference for the resulting receiver LE_PHYs bit * @return HCIStatusCode + * @see getTxPhys() + * @see getRxPhys() + * @see getConnectedLE_PHY() + * @see setConnectedLE_PHY() + * @see BTAdapter::setDefaultLE_PHY() * @since 2.4.0 */ HCIStatusCode getConnectedLE_PHY(LE_PHYs& resTx, LE_PHYs& resRx) noexcept; /** + * Return the Tx LE_PHYs as notified via HCIMetaEventType::LE_PHY_UPDATE_COMPLETE + * or retrieved via getConnectedLE_PHY() + * @see getTxPhys() + * @see getRxPhys() + * @see getConnectedLE_PHY() + * @see setConnectedLE_PHY() + * @see BTAdapter::setDefaultLE_PHY() + * @since 2.4.0 + */ + LE_PHYs getTxPhys() const noexcept { return le_phy_tx; } + + /** + * Return the Rx LE_PHYs as notified via HCIMetaEventType::LE_PHY_UPDATE_COMPLETE + * or retrieved via getConnectedLE_PHY() + * @see getTxPhys() + * @see getRxPhys() + * @see getConnectedLE_PHY() + * @see setConnectedLE_PHY() + * @see BTAdapter::setDefaultLE_PHY() + * @since 2.4.0 + */ + LE_PHYs getRxPhys() const noexcept { return le_phy_rx; } + + /** + * Sets preference of used LE_PHYs for the given connection. + * + * - BT Core Spec v5.2: Vol 4, Part E, 7.8.49 LE Set PHY command + * - BT Core Spec v5.2: Vol 4, Part E, 7.7.65.12 LE PHY Update Complete event + * + * @param tryTx if true, host has preference for given Tx LE_PHYs + * @param tryRx if true, host has preference for given Rx LE_PHYs + * @param Tx transmitter LE_PHYs of preference if tryTx is true, otherwise ignored + * @param Rx receiver LE_PHYs of preference if tryRx is true, otherwise ignored + * @return + * @see getTxPhys() + * @see getRxPhys() + * @see getConnectedLE_PHY() + * @see setConnectedLE_PHY() + * @see BTAdapter::setDefaultLE_PHY() + * @since 2.4.0 + */ + HCIStatusCode setConnectedLE_PHY(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept; + + /** * Disconnect the LE or BREDR peer's GATT and HCI connection. * <p> * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command diff --git a/api/direct_bt/HCIHandler.hpp b/api/direct_bt/HCIHandler.hpp index daec62e5..9341270a 100644 --- a/api/direct_bt/HCIHandler.hpp +++ b/api/direct_bt/HCIHandler.hpp @@ -682,6 +682,10 @@ namespace direct_bt { * <pre> * BT Core Spec v5.2: Vol 4, Part E, 7.8.47 LE Read PHY command * </pre> + * + * Controller shall send a pending HCIMetaEventType::LE_PHY_UPDATE_COMPLETE event with SUCCESS + * after issuing this command in all cases (change or unchanged PHYs). + * * @param conn_handle * @param addressAndType * @param resTx reference for the resulting transmitter LE_PHYs bit @@ -692,6 +696,41 @@ namespace direct_bt { HCIStatusCode le_read_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType, LE_PHYs& resTx, LE_PHYs& resRx) noexcept; + + /** + * Sets default preference of used LE_PHYs for all subsequent LE connections. + * + * BT Core Spec v5.2: Vol 4, Part E, 7.8.48 LE Set Default PHY command + * + * @param tryTx if true, host has preference for given Tx LE_PHYs + * @param tryRx if true, host has preference for given Rx LE_PHYs + * @param Tx transmitter LE_PHYs of preference if tryTx is true, otherwise ignored + * @param Rx receiver LE_PHYs of preference if tryRx is true, otherwise ignored + * @return + * @since 2.4.0 + */ + HCIStatusCode le_set_default_phy(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept; + + /** + * Sets preference of used LE_PHYs for the given connection. + * + * - BT Core Spec v5.2: Vol 4, Part E, 7.8.49 LE Set PHY command + * - BT Core Spec v5.2: Vol 4, Part E, 7.7.65.12 LE PHY Update Complete event + * + * @param conn_handle + * @param peerAddressAndType + * @param tryTx if true, host has preference for given Tx LE_PHYs + * @param tryRx if true, host has preference for given Rx LE_PHYs + * @param Tx transmitter LE_PHYs of preference if tryTx is true, otherwise ignored + * @param Rx receiver LE_PHYs of preference if tryRx is true, otherwise ignored + * @return + * @since 2.4.0 + */ + HCIStatusCode le_set_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType, + const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept; + private: /** * Sets LE advertising parameters. diff --git a/api/direct_bt/HCITypes.hpp b/api/direct_bt/HCITypes.hpp index 1ff9fdcc..a2beb608 100644 --- a/api/direct_bt/HCITypes.hpp +++ b/api/direct_bt/HCITypes.hpp @@ -421,6 +421,7 @@ namespace direct_bt { LE_ENABLE_ENC = 0x2019, LE_READ_PHY = 0x2030, LE_SET_DEFAULT_PHY = 0x2031, + LE_SET_PHY = 0x2032, LE_SET_EXT_ADV_PARAMS = 0x2036, LE_SET_EXT_ADV_DATA = 0x2037, LE_SET_EXT_SCAN_RSP_DATA = 0x2038, @@ -467,13 +468,14 @@ namespace direct_bt { LE_ENABLE_ENC = 39, LE_READ_PHY = 40, LE_SET_DEFAULT_PHY = 41, - LE_SET_EXT_ADV_PARAMS = 42, - LE_SET_EXT_ADV_DATA = 43, - LE_SET_EXT_SCAN_RSP_DATA = 44, - LE_SET_EXT_ADV_ENABLE = 45, - LE_SET_EXT_SCAN_PARAMS = 46, - LE_SET_EXT_SCAN_ENABLE = 47, - LE_EXT_CREATE_CONN = 48 + LE_SET_PHY = 42, + LE_SET_EXT_ADV_PARAMS = 44, + LE_SET_EXT_ADV_DATA = 45, + LE_SET_EXT_SCAN_RSP_DATA = 46, + LE_SET_EXT_ADV_ENABLE = 47, + LE_SET_EXT_SCAN_PARAMS = 48, + LE_SET_EXT_SCAN_ENABLE = 49, + LE_EXT_CREATE_CONN = 50 // etc etc - incomplete }; constexpr uint8_t number(const HCIOpcodeBit rhs) noexcept { diff --git a/api/direct_bt/MgmtTypes.hpp b/api/direct_bt/MgmtTypes.hpp index 475791d2..e5b18ccf 100644 --- a/api/direct_bt/MgmtTypes.hpp +++ b/api/direct_bt/MgmtTypes.hpp @@ -1130,7 +1130,8 @@ namespace direct_bt { HCI_ENC_CHANGED = 0x002e, // direct_bt extension HCIHandler -> listener HCI_ENC_KEY_REFRESH_COMPLETE = 0x002f, // direct_bt extension HCIHandler -> listener HCI_LE_REMOTE_USR_FEATURES = 0x0030, // direct_bt extension HCIHandler -> listener - MGMT_EVENT_TYPE_COUNT = 0x0031 + HCI_LE_PHY_UPDATE_COMPLETE = 0x0031, // direct_bt extension HCIHandler -> listener + MGMT_EVENT_TYPE_COUNT = 0x0032 }; static constexpr uint16_t number(const Opcode rhs) noexcept { return static_cast<uint16_t>(rhs); @@ -2192,6 +2193,45 @@ namespace direct_bt { const uint8_t* getData() const noexcept override { return nullptr; } }; + /** + * mgmt_addr_info { EUI48, uint8_t type }, + * uint8_t Tx (8 Octets) + * uint8_t Rx (8 Octets) + * <p> + * This is a Direct_BT extension for HCI. + * </p> + */ + class MgmtEvtLEPhyUpdateComplete : public MgmtEvent + { + protected: + std::string baseString() const noexcept override { + return MgmtEvent::baseString()+", address="+getAddress().toString()+ + ", addressType "+to_string(getAddressType())+ + ", Tx="+direct_bt::to_string(getTx())+ + ", Rx="+direct_bt::to_string(getRx()); + } + + public: + MgmtEvtLEPhyUpdateComplete(const uint16_t dev_id, const BDAddressAndType& addressAndType, const LE_PHYs Tx, const LE_PHYs Rx) + : MgmtEvent(Opcode::HCI_LE_PHY_UPDATE_COMPLETE, dev_id, 6+1+2) + { + pdu.put_eui48_nc(MGMT_HEADER_SIZE, addressAndType.address); + pdu.put_uint8_nc(MGMT_HEADER_SIZE+6, direct_bt::number(addressAndType.type)); + pdu.put_uint8_nc(MGMT_HEADER_SIZE+6+1, direct_bt::number(Tx)); + pdu.put_uint8_nc(MGMT_HEADER_SIZE+6+2, direct_bt::number(Rx)); + } + + const EUI48& getAddress() const noexcept { return *reinterpret_cast<const EUI48 *>( pdu.get_ptr_nc(MGMT_HEADER_SIZE + 0) ); } // mgmt_addr_info + BDAddressType getAddressType() const noexcept { return static_cast<BDAddressType>(pdu.get_uint8_nc(MGMT_HEADER_SIZE+6)); } // mgmt_addr_info + + LE_PHYs getTx() const noexcept { return static_cast<LE_PHYs>(pdu.get_uint8_nc(MGMT_HEADER_SIZE+6+1)); } + LE_PHYs getRx() const noexcept { return static_cast<LE_PHYs>(pdu.get_uint8_nc(MGMT_HEADER_SIZE+6+2)); } + + jau::nsize_t getDataOffset() const noexcept override { return MGMT_HEADER_SIZE+6+1+2; } + jau::nsize_t getDataSize() const noexcept override { return 0; } + const uint8_t* getData() const noexcept override { return nullptr; } + }; + class MgmtEvtAdapterInfo : public MgmtEvtCmdComplete { protected: diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 63ae04c9..05401b54 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -442,9 +442,15 @@ static void processReadyDevice(std::shared_ptr<BTDevice> device) { bool success = false; { + LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M }; + HCIStatusCode res = device->setConnectedLE_PHY(true /* tryTx */, true /* tryRx */, Tx, Rx); + fprintf_td(stderr, "****** Set Connected LE PHY: status %s: Tx %s, Rx %s\n", + to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); + } + { LE_PHYs resTx, resRx; HCIStatusCode res = device->getConnectedLE_PHY(resTx, resRx); - fprintf_td(stderr, "****** Connected LE PHY: status %s: Tx %s, Rx %s\n", + fprintf_td(stderr, "****** Got Connected LE PHY: status %s: Tx %s, Rx %s\n", to_string(res).c_str(), to_string(resTx).c_str(), to_string(resRx).c_str()); } @@ -671,6 +677,12 @@ static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { return false; } // adapter is powered-on + { + LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M }; + HCIStatusCode res = adapter->setDefaultLE_PHY(true /* tryRx */, true /* tryRx */, Tx, Rx); + fprintf_td(stderr, "****** Set Default LE PHY: status %s: Tx %s, Rx %s\n", + to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); + } std::shared_ptr<AdapterStatusListener> asl(new MyAdapterStatusListener()); adapter->addStatusListener( asl ); // Flush discovered devices after registering our status listener. diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index 734d0aba..46a7a41c 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -231,6 +231,7 @@ bool BTAdapter::enableListening(const bool enable) noexcept { ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDisconnectedHCI)) && ok; ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_FOUND, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceFoundHCI)) && ok; ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_REMOTE_USR_FEATURES, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI)) && ok; + ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_PHY_UPDATE_COMPLETE, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCILEPhyUpdateCompleteHCI)) && ok; ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_CHANGED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCIEncryptionChangedHCI)) && ok; ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_KEY_REFRESH_COMPLETE, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI)) && ok; @@ -606,6 +607,16 @@ HCIStatusCode BTAdapter::reset() noexcept { #endif } + +HCIStatusCode BTAdapter::setDefaultLE_PHY(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept { + if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED + poweredOff(false /* active */); + return HCIStatusCode::NOT_POWERED; + } + return hci.le_set_default_phy(tryTx, tryRx, Tx, Rx); +} + bool BTAdapter::isDeviceWhitelisted(const BDAddressAndType & addressAndType) noexcept { return mgmt.isDeviceWhitelisted(dev_id, addressAndType); } @@ -1524,6 +1535,23 @@ bool BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI(const MgmtEvent& e) noexcept { return true; } +bool BTAdapter::mgmtEvHCILEPhyUpdateCompleteHCI(const MgmtEvent& e) noexcept { + const MgmtEvtLEPhyUpdateComplete &event = *static_cast<const MgmtEvtLEPhyUpdateComplete *>(&e); + + std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType()); + if( nullptr != device ) { + COND_PRINT(debug_event, "BTAdapter::EventHCI:LEPhyUpdateComplete(dev_id %d): %s, %s", + dev_id, event.toString().c_str(), device->toString().c_str()); + + device->notifyLEPhyUpdateComplete(event.getTx(), event.getRx()); + + } else { + WORDY_PRINT("BTAdapter::EventHCI:LEPhyUpdateComplete(dev_id %d): Device not tracked: %s", + dev_id, event.toString().c_str()); + } + return true; +} + bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { const MgmtEvtDeviceDisconnected &event = *static_cast<const MgmtEvtDeviceDisconnected *>(&e); diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index e779cb46..62d046df 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -154,8 +154,10 @@ std::string BTDevice::toString(bool includeDiscoveredServices) const noexcept { std::string out("Device["+to_string(getRole())+", "+addressAndType.toString()+", name['"+name+ "'], age[total "+std::to_string(t0-ts_creation)+", ldisc "+std::to_string(t0-ts_last_discovery)+", lup "+std::to_string(t0-ts_last_update)+ "]ms, connected["+std::to_string(allowDisconnect)+"/"+std::to_string(isConnected)+", handle "+jau::to_hexstring(hciConnHandle)+ - ", sec[lvl "+to_string(pairing_data.sec_level_conn)+", io "+to_string(pairing_data.ioCap_conn)+ - ", auto "+to_string(pairing_data.ioCap_auto)+", pairing "+to_string(pairing_data.mode)+", state "+to_string(pairing_data.state)+"]], rssi "+std::to_string(getRSSI())+ + ", phy[Tx "+direct_bt::to_string(le_phy_tx)+", Rx "+direct_bt::to_string(le_phy_rx)+ + "], sec[lvl "+to_string(pairing_data.sec_level_conn)+", io "+to_string(pairing_data.ioCap_conn)+ + ", auto "+to_string(pairing_data.ioCap_auto)+", pairing "+to_string(pairing_data.mode)+", state "+to_string(pairing_data.state)+ + "]], rssi "+std::to_string(getRSSI())+ ", tx-power "+std::to_string(tx_power)+ ", appearance "+jau::to_hexstring(static_cast<uint16_t>(appearance))+" ("+to_string(appearance)+ "), gap "+direct_bt::to_string(gap_flags)+ @@ -577,6 +579,13 @@ void BTDevice::notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LE_Featur } } +void BTDevice::notifyLEPhyUpdateComplete(const LE_PHYs Tx, const LE_PHYs Rx) noexcept { + DBG_PRINT("BTDevice::notifyLEPhyUpdateComplete: [Tx %s, Rx %s], %s", + direct_bt::to_string(Tx).c_str(), direct_bt::to_string(Rx).c_str(), toString().c_str()); + le_phy_tx = Tx; + le_phy_rx = Rx; +} + void BTDevice::processL2CAPSetup(std::shared_ptr<BTDevice> sthis) { bool callProcessDeviceReady = false; @@ -1680,8 +1689,32 @@ HCIStatusCode BTDevice::getConnectedLE_PHY(LE_PHYs& resTx, LE_PHYs& resRx) noexc } HCIHandler &hci = adapter.getHCI(); - return hci.le_read_phy(hciConnHandle.load(), addressAndType, resTx, resRx); + HCIStatusCode res = hci.le_read_phy(hciConnHandle.load(), addressAndType, resTx, resRx); + if( HCIStatusCode::SUCCESS == res ) { + le_phy_tx = resTx; + le_phy_rx = resRx; + } + return res; +} + +HCIStatusCode BTDevice::setConnectedLE_PHY(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept { + const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor + + if( !isConnected ) { // should not happen + return HCIStatusCode::DISCONNECTED; + } + + if( 0 == hciConnHandle ) { + return HCIStatusCode::UNSPECIFIED_ERROR; + } + if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED + return HCIStatusCode::NOT_POWERED; // powered-off + } + + HCIHandler &hci = adapter.getHCI(); + return hci.le_set_phy(hciConnHandle.load(), addressAndType, tryTx, tryRx, Tx, Rx); } void BTDevice::notifyDisconnected() noexcept { diff --git a/src/direct_bt/HCIHandler.cpp b/src/direct_bt/HCIHandler.cpp index ffb8df81..c0cbc194 100644 --- a/src/direct_bt/HCIHandler.cpp +++ b/src/direct_bt/HCIHandler.cpp @@ -257,6 +257,36 @@ std::unique_ptr<MgmtEvent> HCIHandler::translate(HCIEvent& ev) noexcept { } return std::make_unique<MgmtEvtHCILERemoteUserFeatures>(dev_id, conn->getAddressAndType(), features); } + case HCIMetaEventType::LE_PHY_UPDATE_COMPLETE: { + HCIStatusCode status; + struct le_phy_update_complete { + uint8_t status; + uint16_t handle; + uint8_t tx; + uint8_t rx; + } __packed; + const le_phy_update_complete * ev_cc = getMetaReplyStruct<le_phy_update_complete>(ev, mevt, &status); + if( nullptr == ev_cc ) { + ERR_PRINT("HCIHandler::translate(reader): LE_PHY_UPDATE_COMPLETE: Null reply-struct: %s - %s", + ev.toString().c_str(), toString().c_str()); + return nullptr; + } + const uint16_t handle = jau::le_to_cpu(ev_cc->handle); + const LE_PHYs Tx = static_cast<LE_PHYs>(ev_cc->tx); + const LE_PHYs Rx = static_cast<LE_PHYs>(ev_cc->rx); + const HCIConnectionRef conn = findTrackerConnection(handle); + if( nullptr == conn ) { + WARN_PRINT("HCIHandler::translate(reader): LE_PHY_UPDATE_COMPLETE: Not tracked conn_handle %s", + jau::to_hexstring(handle).c_str()); + return nullptr; + } + if( HCIStatusCode::SUCCESS != status ) { + WARN_PRINT("HCIHandler::translate(reader): LE_PHY_UPDATE_COMPLETE: Failed: Status %s, Handle %s: %s", + to_string(status).c_str(), jau::to_hexstring(handle).c_str(), conn->toString().c_str()); + return nullptr; + } + return std::make_unique<MgmtEvtLEPhyUpdateComplete>(dev_id, conn->getAddressAndType(), Tx, Rx); + } default: return nullptr; } @@ -688,8 +718,10 @@ HCIHandler::HCIHandler(const uint16_t dev_id_, const BTMode btMode_) noexcept filter_set_metaev(HCIMetaEventType::LE_ADVERTISING_REPORT, mask); filter_set_metaev(HCIMetaEventType::LE_REMOTE_FEAT_COMPLETE, mask); filter_set_metaev(HCIMetaEventType::LE_EXT_CONN_COMPLETE, mask); + filter_set_metaev(HCIMetaEventType::LE_PHY_UPDATE_COMPLETE, mask); filter_set_metaev(HCIMetaEventType::LE_EXT_ADV_REPORT, mask); - filter_set_metaev(HCIMetaEventType::LE_CHANNEL_SEL_ALGO, mask); + // filter_set_metaev(HCIMetaEventType::LE_CHANNEL_SEL_ALGO, mask); + #endif filter_put_metaevs(mask); } @@ -716,7 +748,8 @@ HCIHandler::HCIHandler(const uint16_t dev_id_, const BTMode btMode_) noexcept filter_set_opcbit(HCIOpcodeBit::LE_READ_REMOTE_FEATURES, mask); filter_set_opcbit(HCIOpcodeBit::LE_ENABLE_ENC, mask); filter_set_opcbit(HCIOpcodeBit::LE_READ_PHY, mask); - // filter_set_opcbit(HCIOpcodeBit::LE_SET_DEFAULT_PHY, mask); + filter_set_opcbit(HCIOpcodeBit::LE_SET_DEFAULT_PHY, mask); + filter_set_opcbit(HCIOpcodeBit::LE_SET_PHY, mask); filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_ADV_PARAMS, mask); filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_ADV_DATA, mask); filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_RSP_DATA, mask); @@ -1046,7 +1079,7 @@ HCIStatusCode HCIHandler::le_set_scan_param(const bool le_scan_active, le_set_ext_scan_params * cp = req0.getWStruct(); cp->own_address_type = static_cast<uint8_t>(own_mac_type); cp->filter_policy = filter_policy; - cp->scanning_phys = direct_bt::number(LE_PHYs::LE_1M); // TODO: Support LM_CODED? + cp->scanning_phys = direct_bt::number(LE_PHYs::LE_1M); // Only scan on LE_1M for compatibility cp->p1.type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE; cp->p1.interval = jau::cpu_to_le(le_scan_interval); @@ -1236,7 +1269,7 @@ HCIStatusCode HCIHandler::le_create_conn(const EUI48 &peer_bdaddr, cp->own_address_type = static_cast<uint8_t>(own_mac_type); cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type); cp->peer_addr = peer_bdaddr; - cp->phys = direct_bt::number(LE_PHYs::LE_1M); // TODO: Support other PHYs? + cp->phys = direct_bt::number(LE_PHYs::LE_1M); // Only scan on LE_1M for compatibility cp->p1.scan_interval = jau::cpu_to_le(le_scan_interval); cp->p1.scan_window = jau::cpu_to_le(le_scan_window); @@ -1387,6 +1420,11 @@ HCIStatusCode HCIHandler::disconnect(const uint16_t conn_handle, const BDAddress HCIStatusCode HCIHandler::le_read_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType, LE_PHYs& resTx, LE_PHYs& resRx) noexcept { + if( !isLEFeaturesBitSet(le_ll_feats, LE_Features::LE_2M_PHY) ) { + resTx = LE_PHYs::LE_1M; + resRx = LE_PHYs::LE_1M; + return HCIStatusCode::SUCCESS; + } resTx = LE_PHYs::NONE; resRx = LE_PHYs::NONE; @@ -1436,6 +1474,87 @@ HCIStatusCode HCIHandler::le_read_phy(const uint16_t conn_handle, const BDAddres return status; } +HCIStatusCode HCIHandler::le_set_default_phy(const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept { + if( !isLEFeaturesBitSet(le_ll_feats, LE_Features::LE_2M_PHY) ) { + if( tryTx && isLEPHYBitSet(Tx, LE_PHYs::LE_2M) ) { + WARN_PRINT("HCIHandler::le_set_default_phy: LE_2M_PHY no supported, requested Tx %s", to_string(Tx).c_str()); + return HCIStatusCode::INVALID_PARAMS; + } + if( tryRx && isLEPHYBitSet(Rx, LE_PHYs::LE_2M) ) { + WARN_PRINT("HCIHandler::le_set_default_phy: LE_2M_PHY no supported, requested Rx %s", to_string(Rx).c_str()); + return HCIStatusCode::INVALID_PARAMS; + } + } + + if( !isOpen() ) { + ERR_PRINT("HCIHandler::set_default_phy: Not connected %s", toString().c_str()); + return HCIStatusCode::DISCONNECTED; + } + + HCIStatusCode status; + HCIStructCommand<hci_cp_le_set_default_phy> req0(HCIOpcode::LE_SET_DEFAULT_PHY); + hci_cp_le_set_default_phy * cp = req0.getWStruct(); + cp->all_phys = ( tryTx && Tx != LE_PHYs::NONE ? 0b000 : 0b001 ) | + ( tryRx && Rx != LE_PHYs::NONE ? 0b000 : 0b010 ); + cp->tx_phys = number( Tx ); + cp->rx_phys = number( Rx ); + + const hci_rp_status * ev_status; + std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status); + + if( nullptr == ev || nullptr == ev || HCIStatusCode::SUCCESS != status ) { + ERR_PRINT("HCIHandler::le_set_default_phy: LE_SET_PHY: 0x%x (%s) - %s", + number(status), to_string(status).c_str(), toString().c_str()); + } + return status; +} + +HCIStatusCode HCIHandler::le_set_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType, + const bool tryTx, const bool tryRx, + const LE_PHYs Tx, const LE_PHYs Rx) noexcept { + if( !isLEFeaturesBitSet(le_ll_feats, LE_Features::LE_2M_PHY) ) { + if( tryTx && isLEPHYBitSet(Tx, LE_PHYs::LE_2M) ) { + WARN_PRINT("HCIHandler::le_set_phy: LE_2M_PHY no supported, requested Tx %s", to_string(Tx).c_str()); + return HCIStatusCode::INVALID_PARAMS; + } + if( tryRx && isLEPHYBitSet(Rx, LE_PHYs::LE_2M) ) { + WARN_PRINT("HCIHandler::le_set_phy: LE_2M_PHY no supported, requested Rx %s", to_string(Rx).c_str()); + return HCIStatusCode::INVALID_PARAMS; + } + } + + HCIStatusCode status = check_open_connection("le_set_phy", conn_handle, peerAddressAndType); + if( HCIStatusCode::SUCCESS != status ) { + return status; + } + + struct hci_cp_le_set_phy { + uint16_t handle; + __u8 all_phys; + __u8 tx_phys; + __u8 rx_phys; + uint16_t phy_options; + } __packed; + + HCIStructCommand<hci_cp_le_set_phy> req0(HCIOpcode::LE_SET_PHY); + hci_cp_le_set_phy * cp = req0.getWStruct(); + cp->handle = jau::cpu_to_le(conn_handle); + cp->all_phys = ( tryTx && Tx != LE_PHYs::NONE ? 0b000 : 0b001 ) | + ( tryRx && Rx != LE_PHYs::NONE ? 0b000 : 0b010 ); + cp->tx_phys = number( Tx ); + cp->rx_phys = number( Rx ); + cp->phy_options = 0; + + std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status); + + if( nullptr == ev || nullptr == ev || HCIStatusCode::SUCCESS != status ) { + ERR_PRINT("HCIHandler::le_set_phy: LE_SET_PHY: 0x%x (%s) - %s", + number(status), to_string(status).c_str(), toString().c_str()); + } + return status; +} + HCIStatusCode HCIHandler::le_set_adv_param(const EUI48 &peer_bdaddr, const HCILEOwnAddressType own_mac_type, const HCILEOwnAddressType peer_mac_type, diff --git a/src/direct_bt/HCITypes.cpp b/src/direct_bt/HCITypes.cpp index 5355f4cc..f0d90b17 100644 --- a/src/direct_bt/HCITypes.cpp +++ b/src/direct_bt/HCITypes.cpp @@ -198,6 +198,7 @@ std::string to_string(const HCIOGF op) noexcept { X(LE_ENABLE_ENC) \ X(LE_READ_PHY) \ X(LE_SET_DEFAULT_PHY) \ + X(LE_SET_PHY) \ X(LE_SET_EXT_ADV_PARAMS) \ X(LE_SET_EXT_ADV_DATA) \ X(LE_SET_EXT_SCAN_RSP_DATA) \ |