aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2021-09-24 12:59:13 +0200
committerSven Gothel <[email protected]>2021-09-24 12:59:13 +0200
commit61bfb0757a1c337eaf86f9df8b0524bec1b7bc0f (patch)
tree55e81117467e342ba7c6ef7d7b6beb3a43b01655
parent61108682ad95d12f66cc5ee6cca89a5bbf99c5fa (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.hpp20
-rw-r--r--api/direct_bt/BTDevice.hpp53
-rw-r--r--api/direct_bt/HCIHandler.hpp39
-rw-r--r--api/direct_bt/HCITypes.hpp16
-rw-r--r--api/direct_bt/MgmtTypes.hpp42
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp14
-rw-r--r--src/direct_bt/BTAdapter.cpp28
-rw-r--r--src/direct_bt/BTDevice.cpp39
-rw-r--r--src/direct_bt/HCIHandler.cpp127
-rw-r--r--src/direct_bt/HCITypes.cpp1
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) \