aboutsummaryrefslogtreecommitdiffstats
path: root/src/direct_bt
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-06-04 04:11:12 +0200
committerSven Gothel <[email protected]>2020-06-04 04:11:12 +0200
commit73f5b4fc21d7a14aa174f2c48657ca3f2229dda1 (patch)
tree1b455d3e699600eb9f0c1ccc09d10ffd8052929b /src/direct_bt
parent1c449ba2a4cede1e942ff9df181bf96a408d2c67 (diff)
Introduce HCITypes + HCIHandler: Providing C++ basics for full HCI support; Misc..
Introduce HCITypes + HCIHandler: Providing C++ basics for full HCI support ============================================================================ - HCITypes defines the HCI command and event data structures, similar to MgmtTypes and ATTPDUTypes. For more coding efficiency we use template command and event classes, allowing to reuse the HCIIoctl struct types being wrapped in the C++ classes for flow and lifecycle control. HCITypes further includes all essential 'enum class' command opcodes, event types etc, ensuring type safety. - HCIHandler defines the command/event workflow similar to GATTHandler and DBTManager. One HCIHandler per DBTAdapter is being used, each HCIHandler using its event reader thread and ringbuffer. Socket event filtering and manual le-meta filtering ensures reduced workload (DoS safety). Similar to HCITypes, we use template function to utilize the template command and event classes, reuising the HCIIoctl struct types... HCIHandler can be extended for more functionality as well as used to implement our own DBTManager, i.e. Reducing HCIEvents to MgmtEvents with callback support. At least this is a viable option now, if so desired (non-linux, etc). - HCIComm: Contains the core I/O functionality only, dropped all semantic HCI code. The latter resides in HCIHandler now. - DBTAdapter now uses HCIHander instead of HCIComm, renamed field to simply 'hci' Misc.. ======= - reduced all ringbuffer capacity to 128 from 256 - Moved Mgmt*EventCallback from DBTManager to MgmtTypes, as we might want to reuse the MgmtTypes incl callbacks for alternative DBTManager implementations. - Added 'enum class' to underlying number type conversion pattern: <NumberType> number('enum class type') Avoiding the many static_cast<NumberType>(..) directives. - align all Type -> String method names: 'get<Type>String(Type)' - BasicTypes: Add safe uint32_t bitfield mask helper
Diffstat (limited to 'src/direct_bt')
-rw-r--r--src/direct_bt/BTTypes.cpp21
-rw-r--r--src/direct_bt/CMakeLists.txt2
-rw-r--r--src/direct_bt/DBTAdapter.cpp36
-rw-r--r--src/direct_bt/DBTDevice.cpp30
-rw-r--r--src/direct_bt/DBTManager.cpp3
-rw-r--r--src/direct_bt/DBTTypes.cpp6
-rw-r--r--src/direct_bt/GATTHandler.cpp9
-rw-r--r--src/direct_bt/GATTNumbers.cpp2
-rw-r--r--src/direct_bt/HCIComm.cpp581
-rw-r--r--src/direct_bt/HCIHandler.cpp598
-rw-r--r--src/direct_bt/HCITypes.cpp299
11 files changed, 956 insertions, 631 deletions
diff --git a/src/direct_bt/BTTypes.cpp b/src/direct_bt/BTTypes.cpp
index 522a2448..d4fc1a13 100644
--- a/src/direct_bt/BTTypes.cpp
+++ b/src/direct_bt/BTTypes.cpp
@@ -106,6 +106,15 @@ static inline const int8_t * const_uint8_to_const_int8_ptr(const uint8_t* p) {
return static_cast<const int8_t *>( static_cast<void *>( const_cast<uint8_t*>( p ) ) );
}
+std::string direct_bt::BTModeString(const BTMode v) {
+ switch(v) {
+ case BTMode::BT_MODE_DUAL: return "BT_MODE_DUAL";
+ case BTMode::BT_MODE_BREDR: return "BT_MODE_BREDR";
+ case BTMode::BT_MODE_LE: return "BT_MODE_LE";
+ }
+ return "Unknown BT_MODE";
+}
+
#define APPEARANCECAT_ENUM(X) \
X(UNKNOWN) \
X(GENERIC_PHONE) \
@@ -168,7 +177,7 @@ static inline const int8_t * const_uint8_to_const_int8_ptr(const uint8_t* p) {
#define APPEARANCE_CASE_TO_STRING(V) case AppearanceCat::V: return #V;
-std::string direct_bt::AppearanceCatToString(const AppearanceCat v) {
+std::string direct_bt::getAppearanceCatString(const AppearanceCat v) {
switch(v) {
APPEARANCECAT_ENUM(APPEARANCE_CASE_TO_STRING)
default: ; // fall through intended
@@ -217,7 +226,7 @@ std::string ManufactureSpecificData::toString() const {
X(EIRDataType,DEVICE_ID) \
X(EIRDataType,SERVICE_UUID)
-std::string direct_bt::eirDataBitToString(const EIRDataType bit) {
+std::string direct_bt::getEIRDataBitString(const EIRDataType bit) {
switch(bit) {
EIRDATATYPE_ENUM(CASE2_TO_STRING)
default: ; // fall through intended
@@ -225,7 +234,7 @@ std::string direct_bt::eirDataBitToString(const EIRDataType bit) {
return "Unknown EIRDataType Bit";
}
-std::string direct_bt::eirDataMaskToString(const EIRDataType mask) {
+std::string direct_bt::getEIRDataMaskString(const EIRDataType mask) {
const uint32_t one = 1;
bool has_pre = false;
std::string out("[");
@@ -233,7 +242,7 @@ std::string direct_bt::eirDataMaskToString(const EIRDataType mask) {
const EIRDataType settingBit = static_cast<EIRDataType>( one << i );
if( EIRDataType::NONE != ( mask & settingBit ) ) {
if( has_pre ) { out.append(", "); }
- out.append(eirDataBitToString(settingBit));
+ out.append(getEIRDataBitString(settingBit));
has_pre = true;
}
}
@@ -276,7 +285,7 @@ void EInfoReport::addService(std::shared_ptr<uuid_t> const &uuid)
}
std::string EInfoReport::eirDataMaskToString() const {
- return std::string("DataSet"+ direct_bt::eirDataMaskToString(eir_data_mask) );
+ return std::string("DataSet"+ direct_bt::getEIRDataMaskString(eir_data_mask) );
}
std::string EInfoReport::toString() const {
std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]";
@@ -286,7 +295,7 @@ std::string EInfoReport::toString() const {
", evt-type "+std::to_string(evt_type)+", rssi "+std::to_string(rssi)+
", tx-power "+std::to_string(tx_power)+
", dev-class "+uint32HexString(device_class, true)+
- ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+
+ ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+
"), hash["+hash.toString()+
"], randomizer["+randomizer.toString()+
"], device-id[source "+uint16HexString(did_source, true)+
diff --git a/src/direct_bt/CMakeLists.txt b/src/direct_bt/CMakeLists.txt
index 5a8637c1..f16905c9 100644
--- a/src/direct_bt/CMakeLists.txt
+++ b/src/direct_bt/CMakeLists.txt
@@ -15,6 +15,8 @@ set (direct_bt_LIB_SRCS
${PROJECT_SOURCE_DIR}/src/direct_bt/UUID.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/BTTypes.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/HCIComm.cpp
+ ${PROJECT_SOURCE_DIR}/src/direct_bt/HCITypes.cpp
+ ${PROJECT_SOURCE_DIR}/src/direct_bt/HCIHandler.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/L2CAPComm.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/MgmtTypes.cpp
${PROJECT_SOURCE_DIR}/src/direct_bt/DBTManager.cpp
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index 78ad8131..5d364833 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -124,19 +124,19 @@ bool DBTAdapter::validateDevInfo() {
}
DBTAdapter::DBTAdapter()
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(nullptr != mgmt.getDefaultAdapterInfo() ? 0 : -1)
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(nullptr != mgmt.getDefaultAdapterInfo() ? 0 : -1)
{
valid = validateDevInfo();
}
DBTAdapter::DBTAdapter(EUI48 &mac)
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(mgmt.findAdapterInfoIdx(mac))
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(mgmt.findAdapterInfoIdx(mac))
{
valid = validateDevInfo();
}
DBTAdapter::DBTAdapter(const int dev_id)
-: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), dev_id(dev_id)
+: mgmt(DBTManager::get(BTMode::BT_MODE_LE)), btMode(mgmt.getBTMode()), dev_id(dev_id)
{
valid = validateDevInfo();
}
@@ -177,33 +177,33 @@ void DBTAdapter::setBondable(bool value) {
mgmt.setMode(dev_id, MgmtOpcode::SET_BONDABLE, value ? 1 : 0);
}
-std::shared_ptr<HCIComm> DBTAdapter::openHCI()
+std::shared_ptr<HCIHandler> 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() ) {
+ if( nullptr != hci ) {
+ if( hci->isOpen() ) {
DBG_PRINT("DBTAdapter::openHCI: Already open");
- return hciComm;
+ return hci;
}
- hciComm = nullptr;
+ hci = nullptr;
}
- HCIComm *s = new HCIComm(dev_id, HCI_CHANNEL_RAW, HCIDefaults::HCI_TO_SEND_REQ_POLL_MS);
+ HCIHandler *s = new HCIHandler(btMode, dev_id, HCIHandler::Defaults::HCI_COMMAND_REPLY_TIMEOUT);
if( !s->isOpen() ) {
delete s;
- ERR_PRINT("Could not open HCIComm: %s", toString().c_str());
+ ERR_PRINT("Could not open HCIHandler: %s of %s", s->toString().c_str(), toString().c_str());
return nullptr;
}
- hciComm = std::shared_ptr<HCIComm>( s );
- return hciComm;
+ hci = std::shared_ptr<HCIHandler>( s );
+ return hci;
}
-std::shared_ptr<HCIComm> DBTAdapter::getHCI() const {
+std::shared_ptr<HCIHandler> 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;
+ return hci;
}
bool DBTAdapter::closeHCI()
@@ -211,13 +211,13 @@ 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() ) {
+ if( nullptr == hci || !hci->isOpen() ) {
DBG_PRINT("DBTAdapter::closeHCI: Not open");
return false;
}
disconnectAllDevices(); // FIXME ????
- hciComm->close();
- hciComm = nullptr;
+ hci->close();
+ hci = nullptr;
DBG_PRINT("DBTAdapter::closeHCI: XXX");
return true;
}
@@ -423,7 +423,7 @@ std::shared_ptr<DBTDevice> DBTAdapter::findSharedDevice (EUI48 const & mac) cons
}
std::string DBTAdapter::toString() const {
- std::string out("Adapter["+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+", "+javaObjectToString()+"]");
+ std::string out("Adapter["+BTModeString(btMode)+", "+getAddressString()+", '"+getName()+"', id="+std::to_string(dev_id)+", "+javaObjectToString()+"]");
std::vector<std::shared_ptr<DBTDevice>> devices = getDiscoveredDevices();
if(devices.size() > 0 ) {
out.append("\n");
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index 44af3b03..848aa33c 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -123,7 +123,7 @@ std::string DBTDevice::toString(bool includeDiscoveredServices) const {
"'], age "+std::to_string(t0-ts_creation)+" ms, lup "+std::to_string(t0-ts_update)+
" ms, connected "+std::to_string(isConnected)+", rssi "+std::to_string(getRSSI())+
", tx-power "+std::to_string(tx_power)+
- ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+
+ ", appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+
"), "+msdstr+", "+javaObjectToString()+"]");
if(includeDiscoveredServices && services.size() > 0 ) {
out.append("\n");
@@ -250,9 +250,9 @@ uint16_t DBTDevice::connectLE(HCIAddressType peer_mac_type, HCIAddressType own_m
}
const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor
- std::shared_ptr<HCIComm> hciComm = adapter.openHCI();
- if( nullptr == hciComm || !hciComm->isOpen() ) {
- ERR_PRINT("DBTDevice::connectLE: Opening adapter's HCIComm failed: %s", toString().c_str());
+ std::shared_ptr<HCIHandler> hci = adapter.openHCI();
+ if( nullptr == hci || !hci->isOpen() ) {
+ ERR_PRINT("DBTDevice::connectLE: Opening adapter's HCI failed: %s", toString().c_str());
return 0;
}
if( !isLEAddressType() ) {
@@ -265,10 +265,10 @@ uint16_t DBTDevice::connectLE(HCIAddressType peer_mac_type, HCIAddressType own_m
DBTManager & mngr = adapter.getManager();
mngr.create_connection(adapter.dev_id, address, addressType);
}
- HCIErrorCode status = hciComm->le_create_conn(&hciConnHandle, address,
- peer_mac_type, own_mac_type,
- le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
- conn_latency, supervision_timeout);
+ HCIErrorCode status = hci->le_create_conn(&hciConnHandle, address,
+ peer_mac_type, own_mac_type,
+ le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
+ conn_latency, supervision_timeout);
#if 0
if( HCIErrorCode::CONNECTION_ALREADY_EXISTS == status ) {
INFO_PRINT("DBTDevice::connectLE: Connection already exists: status 0x%2.2X (%s) on %s",
@@ -302,9 +302,9 @@ uint16_t DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_o
return 0;
}
const std::lock_guard<std::recursive_mutex> lock(adapter.mtx_hci); // RAII-style acquire and relinquish via destructor
- std::shared_ptr<HCIComm> hciComm = adapter.openHCI();
- if( nullptr == hciComm || !hciComm->isOpen() ) {
- ERR_PRINT("DBTDevice::connectBREDR: Opening adapter's HCIComm failed: %s", toString().c_str());
+ std::shared_ptr<HCIHandler> hci = adapter.openHCI();
+ if( nullptr == hci || !hci->isOpen() ) {
+ ERR_PRINT("DBTDevice::connectBREDR: Opening adapter's HCI failed: %s", toString().c_str());
return 0;
}
if( !isBREDRAddressType() ) {
@@ -318,7 +318,7 @@ uint16_t DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_o
mngr.create_connection(adapter.dev_id, address, addressType);
}
- HCIErrorCode status = hciComm->create_conn(&hciConnHandle, address, pkt_type, clock_offset, role_switch);
+ HCIErrorCode status = hci->create_conn(&hciConnHandle, address, pkt_type, clock_offset, role_switch);
if ( HCIErrorCode::SUCCESS != status ) {
ERR_PRINT("DBTDevice::connectBREDR: Could not create connection: status 0x%2.2X (%s), errno %d %s on %s",
static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno), toString().c_str());
@@ -366,7 +366,7 @@ void DBTDevice::disconnect(const bool sentFromManager, const bool ioErrorCause,
disconnectGATT();
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();
+ std::shared_ptr<HCIHandler> hci = adapter.getHCI();
if( !isConnected ) {
DBG_PRINT("DBTDevice::disconnect: Skip disconnect: Not connected: %s", toString().c_str());
@@ -384,12 +384,12 @@ void DBTDevice::disconnect(const bool sentFromManager, const bool ioErrorCause,
goto skip_hci_disconnect;
}
- if( nullptr == hciComm || !hciComm->isOpen() ) {
+ if( nullptr == hci || !hci->isOpen() ) {
DBG_PRINT("DBTDevice::disconnect: Skip HCI disconnect: HCI not Open: %s", toString().c_str());
goto skip_hci_disconnect;
}
- if( !hciComm->disconnect(hciConnHandle, reason) ) {
+ if( HCIErrorCode::SUCCESS != hci->disconnect(hciConnHandle, reason) ) {
DBG_PRINT("DBTDevice::disconnect: handle 0x%X, errno %d %s", hciConnHandle, errno, strerror(errno));
}
diff --git a/src/direct_bt/DBTManager.cpp b/src/direct_bt/DBTManager.cpp
index d40db50b..546e1b96 100644
--- a/src/direct_bt/DBTManager.cpp
+++ b/src/direct_bt/DBTManager.cpp
@@ -106,8 +106,9 @@ void DBTManager::mgmtReaderThreadImpl() {
}
}
- INFO_PRINT("DBTManager::reader: Ended");
+ INFO_PRINT("DBTManager::reader: Ended. Ring has %d entries flushed", mgmtEventRing.getSize());
mgmtReaderRunning = false;
+ mgmtEventRing.clear();
}
void DBTManager::sendMgmtEvent(std::shared_ptr<MgmtEvent> event) {
diff --git a/src/direct_bt/DBTTypes.cpp b/src/direct_bt/DBTTypes.cpp
index a880570b..6b5db0e4 100644
--- a/src/direct_bt/DBTTypes.cpp
+++ b/src/direct_bt/DBTTypes.cpp
@@ -72,7 +72,7 @@ using namespace direct_bt;
X(AdapterSetting,STATIC_ADDRESS) \
X(AdapterSetting,PHY_CONFIGURATION)
-std::string direct_bt::adapterSettingBitToString(const AdapterSetting settingBit) {
+std::string direct_bt::getAdapterSettingBitString(const AdapterSetting settingBit) {
switch(settingBit) {
SETTING_ENUM(CASE2_TO_STRING)
default: ; // fall through intended
@@ -80,7 +80,7 @@ std::string direct_bt::adapterSettingBitToString(const AdapterSetting settingBit
return "Unknown Setting Bit";
}
-std::string direct_bt::adapterSettingsToString(const AdapterSetting settingMask) {
+std::string direct_bt::getAdapterSettingsString(const AdapterSetting settingMask) {
const uint32_t one = 1;
bool has_pre = false;
std::string out("[");
@@ -88,7 +88,7 @@ std::string direct_bt::adapterSettingsToString(const AdapterSetting settingMask)
const AdapterSetting settingBit = static_cast<AdapterSetting>( one << i );
if( AdapterSetting::NONE != ( settingMask & settingBit ) ) {
if( has_pre ) { out.append(", "); }
- out.append(adapterSettingBitToString(settingBit));
+ out.append(getAdapterSettingBitString(settingBit));
has_pre = true;
}
}
diff --git a/src/direct_bt/GATTHandler.cpp b/src/direct_bt/GATTHandler.cpp
index f6ec93a2..23763982 100644
--- a/src/direct_bt/GATTHandler.cpp
+++ b/src/direct_bt/GATTHandler.cpp
@@ -253,8 +253,9 @@ void GATTHandler::l2capReaderThreadImpl() {
}
}
- INFO_PRINT("l2capReaderThreadImpl Ended");
+ INFO_PRINT("l2capReaderThreadImpl Ended. Ring has %d entries flushed", attPDURing.getSize());
l2capReaderRunning = false;
+ attPDURing.clear();
disconnect(ioErrorCause);
}
@@ -381,7 +382,7 @@ std::shared_ptr<const AttPDUMsg> GATTHandler::sendWithReply(const AttPDUMsg & ms
send( msg );
// Ringbuffer read is thread safe
- std::shared_ptr<const AttPDUMsg> res = receiveNext();
+ std::shared_ptr<const AttPDUMsg> res = attPDURing.getBlocking(replyTimeoutMS);
if( nullptr == res ) {
errno = ETIMEDOUT;
ERR_PRINT("GATTHandler::send: nullptr result (timeout): req %s to %s", msg.toString().c_str(), deviceString.c_str());
@@ -391,10 +392,6 @@ std::shared_ptr<const AttPDUMsg> GATTHandler::sendWithReply(const AttPDUMsg & ms
return res;
}
-std::shared_ptr<const AttPDUMsg> GATTHandler::receiveNext() {
- return attPDURing.getBlocking(replyTimeoutMS);
-}
-
uint16_t GATTHandler::exchangeMTU(const uint16_t clientMaxMTU) {
/***
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.3.1 Exchange MTU (Server configuration)
diff --git a/src/direct_bt/GATTNumbers.cpp b/src/direct_bt/GATTNumbers.cpp
index 11287968..bbcb944f 100644
--- a/src/direct_bt/GATTNumbers.cpp
+++ b/src/direct_bt/GATTNumbers.cpp
@@ -368,7 +368,7 @@ std::string PeriphalPreferredConnectionParameters::toString() const {
}
std::string GenericAccess::toString() const {
- return "'"+deviceName+"'[appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+AppearanceCatToString(appearance)+"), "+
+ return "'"+deviceName+"'[appearance "+uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+"), "+
prefConnParam.toString()+"]";
}
diff --git a/src/direct_bt/HCIComm.cpp b/src/direct_bt/HCIComm.cpp
index d1e8b2d2..aedbad01 100644
--- a/src/direct_bt/HCIComm.cpp
+++ b/src/direct_bt/HCIComm.cpp
@@ -50,87 +50,6 @@ extern "C" {
namespace direct_bt {
-#define HCI_ERROR_CODE(X) \
- X(SUCCESS) \
- X(UNKNOWN_HCI_COMMAND) \
- X(UNKNOWN_CONNECTION_IDENTIFIER) \
- X(HARDWARE_FAILURE) \
- X(PAGE_TIMEOUT) \
- X(AUTHENTICATION_FAILURE) \
- X(PIN_OR_KEY_MISSING) \
- X(MEMORY_CAPACITY_EXCEEDED) \
- X(CONNECTION_TIMEOUT) \
- X(CONNECTION_LIMIT_EXCEEDED) \
- X(SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED) \
- X(CONNECTION_ALREADY_EXISTS) \
- X(COMMAND_DISALLOWED) \
- X(CONNECTION_REJECTED_LIMITED_RESOURCES) \
- X(CONNECTION_REJECTED_SECURITY) \
- X(CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR) \
- X(CONNECTION_ACCEPT_TIMEOUT_EXCEEDED) \
- X(UNSUPPORTED_FEATURE_OR_PARAM_VALUE) \
- X(INVALID_HCI_COMMAND_PARAMETERS) \
- X(REMOTE_USER_TERMINATED_CONNECTION) \
- X(REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES) \
- X(REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF) \
- X(CONNECTION_TERMINATED_BY_LOCAL_HOST) \
- X(REPEATED_ATTEMPTS) \
- X(PAIRING_NOT_ALLOWED) \
- X(UNKNOWN_LMP_PDU) \
- X(UNSUPPORTED_REMOTE_OR_LMP_FEATURE) \
- X(SCO_OFFSET_REJECTED) \
- X(SCO_INTERVAL_REJECTED) \
- X(SCO_AIR_MODE_REJECTED) \
- X(INVALID_LMP_OR_LL_PARAMETERS) \
- X(UNSPECIFIED_ERROR) \
- X(UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE) \
- X(ROLE_CHANGE_NOT_ALLOWED) \
- X(LMP_OR_LL_RESPONSE_TIMEOUT) \
- X(LMP_OR_LL_COLLISION) \
- X(LMP_PDU_NOT_ALLOWED) \
- X(ENCRYPTION_MODE_NOT_ACCEPTED) \
- X(LINK_KEY_CANNOT_BE_CHANGED) \
- X(REQUESTED_QOS_NOT_SUPPORTED) \
- X(INSTANT_PASSED) \
- X(PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) \
- X(DIFFERENT_TRANSACTION_COLLISION) \
- X(QOS_UNACCEPTABLE_PARAMETER) \
- X(QOS_REJECTED) \
- X(CHANNEL_ASSESSMENT_NOT_SUPPORTED) \
- X(INSUFFICIENT_SECURITY) \
- X(PARAMETER_OUT_OF_RANGE) \
- X(ROLE_SWITCH_PENDING) \
- X(RESERVED_SLOT_VIOLATION) \
- X(ROLE_SWITCH_FAILED) \
- X(EIR_TOO_LARGE) \
- X(SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST) \
- X(HOST_BUSY_PAIRING) \
- X(CONNECTION_REJECTED_NO_SUITABLE_CHANNEL) \
- X(CONTROLLER_BUSY) \
- X(UNACCEPTABLE_CONNECTION_PARAM) \
- X(ADVERTISING_TIMEOUT) \
- X(CONNECTION_TERMINATED_MIC_FAILURE) \
- X(CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT) \
- X(MAX_CONNECTION_FAILED) \
- X(COARSE_CLOCK_ADJ_REJECTED) \
- X(TYPE0_SUBMAP_NOT_DEFINED) \
- X(UNKNOWN_ADVERTISING_IDENTIFIER) \
- X(LIMIT_REACHED) \
- X(OPERATION_CANCELLED_BY_HOST) \
- X(PACKET_TOO_LONG) \
- X(INTERNAL_FAILURE) \
- X(UNKNOWN)
-
-#define HCI_ERROR_CODE_CASE_TO_STRING(V) case HCIErrorCode::V: return #V;
-
-std::string getHCIErrorCodeString(const HCIErrorCode ec) {
- switch(ec) {
- HCI_ERROR_CODE(HCI_ERROR_CODE_CASE_TO_STRING)
- default: ; // fall through intended
- }
- return "Unknown HCI error code";
-}
-
int HCIComm::hci_open_dev(const uint16_t dev_id, const uint16_t channel)
{
sockaddr_hci a;
@@ -263,504 +182,4 @@ errout:
return -1;
}
-bool HCIComm::send_cmd(const uint16_t opcode, const void *command, const uint8_t command_len)
-{
- if( 0 > _dd ) {
- ERR_PRINT("hci_send_cmd: device not open");
- return false;
- }
- const uint8_t type = HCI_COMMAND_PKT;
- hci_command_hdr hc;
- struct iovec iv[3];
- int ivn;
- int bw=0;
-
- hc.opcode = cpu_to_le(opcode);
- hc.plen= command_len;
-
- iv[0].iov_base = (void*)&type;
- iv[0].iov_len = 1;
- iv[1].iov_base = (void*)&hc;
- iv[1].iov_len = HCI_COMMAND_HDR_SIZE;
- ivn = 2;
-
- if (command_len) {
- iv[2].iov_base = (void*)command;
- iv[2].iov_len = command_len;
- ivn = 3;
- }
-
-#ifdef VERBOSE_ON
- {
- fprintf(stderr, "hci_send_cmd: type 0x%2.2X, opcode 0x%X, plen %d\n", type, hc.opcode, command_len);
- std::string paramstr = command_len > 0 ? bytesHexString((uint8_t*)command, 0, command_len, false /* lsbFirst */, true /* leading0X */) : "";
- fprintf(stderr, "hci_send_cmd: param: %s\n", paramstr.c_str());
- }
-#endif
-
- while ( ( bw = ::writev(_dd, iv, ivn) ) < 0 ) {
- ERR_PRINT("hci_send_cmd: writev res %d", bw);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- return false;
- }
- DBG_PRINT("hci_send_cmd: writev: %d", bw);
- return true;
-}
-
-#define _HCI_PKT_TRY_COUNT 10
-
-HCIErrorCode HCIComm::send_req(const uint16_t opcode, const void *command, const uint8_t command_len,
- const uint16_t exp_event, void *response, const uint8_t response_len)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_send_req: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- uint8_t buf[HCI_MAX_EVENT_SIZE];
- const uint16_t opcode_le16 = cpu_to_le(opcode);
- hci_ufilter nf, of;
- socklen_t olen;
- int err, tryCount=0;
-
- DBG_PRINT("hci_send_req: opcode 0x%X ", opcode_le16);
-
- olen = sizeof(of);
- if (getsockopt(_dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
- ERR_PRINT("hci_send_req");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
-
- filter_clear(&nf);
- filter_set_ptype(HCI_EVENT_PKT, &nf);
- filter_set_event(HCI_EV_CMD_STATUS, &nf);
- filter_set_event(HCI_EV_CMD_COMPLETE, &nf);
- filter_set_event(HCI_EV_LE_META, &nf);
- filter_set_event(exp_event, &nf);
- filter_set_opcode(opcode_le16, &nf);
- if (setsockopt(_dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
- ERR_PRINT("hci_send_req");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
-
- int _timeoutMS = timeoutMS;
- HCIErrorCode res = HCIErrorCode::INTERNAL_FAILURE;
-
- if ( !send_cmd(opcode, command, command_len) ) {
- ERR_PRINT("hci_send_req");
- goto failed;
- }
-
- // Reading up to 10 packets for HCI responses,
- while ( _HCI_PKT_TRY_COUNT > tryCount++ ) {
- if ( _timeoutMS ) {
- struct pollfd p;
- int n;
-
- p.fd = _dd; p.events = POLLIN;
-#if 0
- sigset_t sigmask;
- sigemptyset(&sigmask);
- sigaddset(&sigmask, SIGALRM);
- struct timespec timeout_ts;
- timeout_ts.tv_sec=0;
- timeout_ts.tv_nsec=(long)_timeoutMS*1000000L;
- while ((n = ppoll(&p, 1, &timeout_ts, &sigmask)) < 0) {
-#else
- while ((n = poll(&p, 1, _timeoutMS)) < 0) {
-#endif
- ERR_PRINT("HCIComm::send_req(dev_id %d, channel %d): poll: res %d", dev_id, channel, n);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- goto failed;
- }
-
- if (!n) {
- DBG_PRINT("hci_send_req: poll: timeout");
- errno = ETIMEDOUT;
- goto failed;
- }
-
- _timeoutMS -= _timeoutMS/_HCI_PKT_TRY_COUNT; // reduce timeout consecutively
- if ( _timeoutMS < 0 ) {
- _timeoutMS = 0;
- }
- }
-
- int len;
-
- while ((len = ::read(_dd, buf, sizeof(buf))) < 0) {
- ERR_PRINT("HCIComm::send_req(dev_id %d, channel %d): read: res %d", dev_id, channel, len);
- if (errno == EAGAIN || errno == EINTR) {
- continue;
- }
- goto failed;
- }
-
- const hci_event_hdr *hdr = static_cast<hci_event_hdr *>(static_cast<void *>(buf + 1));
- const uint8_t * ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
- len -= (1 + HCI_EVENT_HDR_SIZE);
-
- DBG_PRINT("hci_send_req: read: %d bytes, evt 0x%2.2X", len, hdr->evt);
-
- switch (hdr->evt) {
- case HCI_EV_CMD_STATUS: {
- const hci_ev_cmd_status *cs = static_cast<const hci_ev_cmd_status *>(static_cast<const void *>( ptr ));
- const HCIErrorCode status = static_cast<HCIErrorCode>(cs->status);
-
- DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: opcode 0x%X, exp_event 0x%X, status 0x%2.2X (%s), rlen %d/%d",
- cs->opcode, exp_event,
- cs->status, getHCIErrorCodeString(status).c_str(),
- response_len, len);
-
- if (cs->opcode != opcode_le16) {
- continue; // next packet
- }
-
- if (exp_event != HCI_EV_CMD_STATUS) {
- if ( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- DBG_PRINT("hci_send_req: event exp 0x%X != has 0x%X, error status 0x%2.2X (%s)", exp_event,
- hdr->evt, cs->status, getHCIErrorCodeString(status).c_str());
- res = status;
- goto failed;
- }
- continue; // next packet
- }
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_CMD_STATUS: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- case HCI_EV_CMD_COMPLETE: {
- const hci_ev_cmd_complete *cc = static_cast<const hci_ev_cmd_complete *>(static_cast<const void *>( ptr ));
-
- DBG_PRINT("hci_send_req: HCI_EV_CMD_COMPLETE: opcode 0x%X (equal %d), exp_event 0x%X, r-len %d/%d",
- cc->opcode, (cc->opcode == opcode_le16), exp_event, response_len, len);
-
- if (cc->opcode != opcode_le16) {
- continue; // next packet
- }
-
- ptr += sizeof(hci_ev_cmd_complete);
- len -= sizeof(hci_ev_cmd_complete);
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_CMD_COMPLETE: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- case HCI_EV_LE_META: {
- const hci_ev_le_meta *me = static_cast<const hci_ev_le_meta *>(static_cast<const void *>( ptr ));
-
- DBG_PRINT("hci_send_req: HCI_EV_LE_META: subevent 0x%X, exp_event 0x%X (equal %d), r-len %d/%d",
- me->subevent, exp_event, (me->subevent == exp_event), response_len, len);
-
- if (me->subevent != exp_event) {
- continue; // next packet
- }
-
- ptr += 1;
- len -= 1;
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: HCI_EV_LE_META: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
-
- default: {
- DBG_PRINT("hci_send_req: DEFAULT: evt 0x%X, exp_event 0x%X (equal %d), r-len %d/%d",
- hdr->evt, exp_event, (hdr->evt == exp_event), response_len, len);
-
- if (hdr->evt != exp_event) {
- continue; // next packet
- }
-
- const int rlen = MIN(len, response_len);
- memcpy(response, ptr, rlen);
- DBG_PRINT("hci_send_req: DEFAULT: copied %d bytes: %s",
- rlen, bytesHexString((uint8_t*)ptr, 0, rlen, false /* lsbFirst */, true /* leading0X */).c_str());
- goto done;
- }
- } // switch event
- } // while packets ...
- errno = ETIMEDOUT;
-
-failed:
- err = errno;
- setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
- errno = err;
- return res;
-
-done:
- setsockopt(_dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
- return HCIErrorCode::SUCCESS;
-}
-
-bool HCIComm::disconnect(const uint16_t le_conn_handle, const HCIErrorCode reason)
-{
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command */
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- DBG_PRINT("hci_disconnect: handle 0x%x, reason 0x%x (%s)",
- le_conn_handle, static_cast<uint8_t>(reason), getHCIErrorCodeString(reason).c_str());
- if( 0 > _dd ) {
- return true;
- }
- if( 0 == le_conn_handle ) {
- return true;
- }
- hci_ev_disconn_complete rp;
- hci_cp_disconnect cp;
-
- bzero(&cp, sizeof(cp));
- cp.handle = le_conn_handle;
- cp.reason = static_cast<uint8_t>(reason);
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_DISCONNECT), &cp, sizeof(cp),
- HCI_EV_DISCONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_disconnect: 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_disconnect: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-bool HCIComm::le_set_scan_enable(const uint8_t enable, const uint8_t filter_dup) {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_set_scan_enable(%d): device not open", enable);
- return false;
- }
- hci_cp_le_set_scan_enable cp;
- HCIErrorCode status;
-
- bzero(&cp, sizeof(cp));
- cp.enable = enable;
- cp.filter_dup = filter_dup;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_ENABLE), &cp, sizeof(cp),
- 0, &status, sizeof(status) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_set_scan_enable(%d): 0x%2.2X (%s), errno %d %s",
- enable, static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_set_scan_enable(%d): error status 0x%2.2X (%s), errno %d %s",
- enable, static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-bool HCIComm::le_set_scan_parameters(const uint8_t type, const uint16_t interval,
- const uint16_t window, const uint8_t own_type,
- const uint8_t filter)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_set_scan_parameters: device not open");
- return false;
- }
- hci_cp_le_set_scan_param cp;
- HCIErrorCode status;
-
- bzero(&cp, sizeof(cp));
- cp.type = type;
- cp.interval = cpu_to_le(interval);
- cp.window = cpu_to_le(window);
- cp.own_address_type = own_type;
- cp.filter_policy = filter;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_SET_SCAN_PARAM), &cp, sizeof(cp),
- 0, &status, sizeof(status) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_set_scan_parameters: 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return false;
- }
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_set_scan_parameters: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(status), getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return false;
- }
- return true;
-}
-
-void HCIComm::le_disable_scan() {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- return;
- }
- if( !le_scanning ) {
- return;
- }
- const uint8_t filter_dup = 0x01;
-
- if( !le_set_scan_enable(0x00, filter_dup) ) {
- ERR_PRINT("Stop scan failed");
- } else {
- le_scanning = false;
- }
-}
-
-bool HCIComm::le_enable_scan(const HCIAddressType own_type,
- const uint16_t interval, const uint16_t window) {
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( 0 > _dd ) {
- DBG_PRINT("hci_le_enable_scan: device not open");
- return false;
- }
- if( le_scanning ) {
- DBG_PRINT("hci_le_enable_scan: device already le-scanning");
- return false;
- }
- const uint8_t scan_type = 0x01;
- // const uint8_t filter_type = 0;
- const uint8_t filter_policy = 0x00;
- const uint8_t filter_dup = 0x01;
-
- bool ok = le_set_scan_parameters(scan_type, interval, window,
- own_type, filter_policy);
- if ( !ok ) {
- ERR_PRINT("Set scan parameters failed");
- return false;
- }
-
- ok = le_set_scan_enable(0x01, filter_dup);
- if ( !ok ) {
- ERR_PRINT("Start scan failed");
- return false;
- }
- le_scanning = true;
- return true;
-}
-
-HCIErrorCode HCIComm::le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
- const HCIAddressType peer_mac_type,
- const HCIAddressType own_mac_type,
- const uint16_t le_scan_interval, const uint16_t le_scan_window,
- const uint16_t conn_interval_min, const uint16_t conn_interval_max,
- const uint16_t conn_latency, const uint16_t supervision_timeout)
-{
-
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command */
- /** BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event: 7.7.65.1 LE Connection Complete event */
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( nullptr != handle_return ) {
- *handle_return = 0;
- }
- if( 0 > _dd ) {
- ERR_PRINT("hci_le_create_conn: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- hci_cp_le_create_conn cp;
- hci_ev_le_conn_complete rp;
-
- const uint16_t min_ce_length = 0x0000;
- const uint16_t max_ce_length = 0x0000;
- const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr*
-
- bzero((void*)&cp, sizeof(cp));
- cp.scan_interval = cpu_to_le(le_scan_interval);
- cp.scan_window = cpu_to_le(le_scan_window);
- cp.filter_policy = initiator_filter;
- cp.peer_addr_type = peer_mac_type;
- cp.peer_addr = peer_bdaddr;
- cp.own_address_type = own_mac_type;
- cp.conn_interval_min = cpu_to_le(conn_interval_min);
- cp.conn_interval_max = cpu_to_le(conn_interval_max);
- cp.conn_latency = cpu_to_le(conn_latency);
- cp.supervision_timeout = cpu_to_le(supervision_timeout);
- cp.min_ce_len = cpu_to_le(min_ce_length);
- cp.max_ce_len = cpu_to_le(max_ce_length);
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LE_CTL, HCI_OP_LE_CREATE_CONN), &cp, sizeof(cp),
- HCI_EV_LE_CONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::COMMAND_DISALLOWED == res ) {
- WARN_PRINT("hci_le_create_conn: COMMAND_DISALLOWED reply, connect may still be in progress. error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_le_create_conn: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return status;
- }
- DBG_PRINT("hci_le_create_conn: Success: handle 0x%x", rp.handle);
- if( nullptr != handle_return ) {
- *handle_return = rp.handle;
- }
- return HCIErrorCode::SUCCESS;
-}
-
-HCIErrorCode HCIComm::create_conn(uint16_t * handle_return, const EUI48 &bdaddr, const uint16_t pkt_type,
- const uint16_t clock_offset, const uint8_t role_switch)
-{
- const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
- if( nullptr != handle_return ) {
- *handle_return = 0;
- }
- if( 0 > _dd ) {
- ERR_PRINT("hci_create_conn: device not open");
- return HCIErrorCode::INTERNAL_FAILURE;
- }
- hci_cp_create_conn cp;
- hci_ev_conn_complete rp;
-
- bzero((void*)&cp, sizeof(cp));
- cp.bdaddr = bdaddr;
- cp.pkt_type = cpu_to_le((uint16_t)(pkt_type & (uint16_t)ACL_PTYPE_MASK)); /* TODO OK excluding SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) ? */
- cp.pscan_rep_mode = 0x02; /* TODO magic? */
- cp.pscan_mode = 0x00; /* TODO magic? */
- cp.clock_offset = cpu_to_le(clock_offset);
- cp.role_switch = role_switch;
-
- HCIErrorCode res = send_req( hci_opcode_pack(OGF_LINK_CTL, HCI_OP_CREATE_CONN), &cp, sizeof(cp),
- HCI_EV_CONN_COMPLETE, &rp, sizeof(rp) );
- if( HCIErrorCode::SUCCESS != res ) {
- ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s",
- static_cast<uint8_t>(res), getHCIErrorCodeString(res).c_str(), errno, strerror(errno));
- return res;
- }
- HCIErrorCode status = static_cast<HCIErrorCode>(rp.status);
- if( HCIErrorCode::SUCCESS != status ) {
- errno = EIO;
- ERR_PRINT("hci_create_conn: error status 0x%2.2X (%s), errno %d %s",
- rp.status, getHCIErrorCodeString(status).c_str(), errno, strerror(errno));
- return status;
- }
- if( nullptr != handle_return ) {
- *handle_return = rp.handle;
- }
- return HCIErrorCode::SUCCESS;
-}
-
} /* namespace direct_bt */
diff --git a/src/direct_bt/HCIHandler.cpp b/src/direct_bt/HCIHandler.cpp
new file mode 100644
index 00000000..5b4688a1
--- /dev/null
+++ b/src/direct_bt/HCIHandler.cpp
@@ -0,0 +1,598 @@
+/*
+ * 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 <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+#include <cstdio>
+
+#include <algorithm>
+
+// #define SHOW_LE_ADVERTISING 1
+
+// #define PERF_PRINT_ON 1
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
+#include "BTIoctl.hpp"
+
+#include "HCIIoctl.hpp"
+#include "HCIComm.hpp"
+#include "HCIHandler.hpp"
+#include "DBTTypes.hpp"
+#include "BasicAlgos.hpp"
+
+extern "C" {
+ #include <inttypes.h>
+ #include <unistd.h>
+ #include <poll.h>
+ #include <signal.h>
+}
+
+using namespace direct_bt;
+
+const pid_t HCIHandler::pidSelf = getpid();
+
+void HCIHandler::hciReaderThreadImpl() {
+ hciReaderShallStop = false;
+ hciReaderRunning = true;
+ INFO_PRINT("HCIHandler::reader: Started");
+
+ while( !hciReaderShallStop ) {
+ int len;
+ if( !comm.isOpen() ) {
+ // not open
+ ERR_PRINT("HCIHandler::reader: Not connected");
+ hciReaderShallStop = true;
+ break;
+ }
+
+ len = comm.read(rbuffer.get_wptr(), rbuffer.getSize());
+ if( 0 < len ) {
+ const uint16_t paramSize = len >= 3 ? rbuffer.get_uint8(2) : 0;
+ if( len < number(HCIConstU8::EVENT_HDR_SIZE) + paramSize ) {
+ WARN_PRINT("HCIHandler::reader: length mismatch %d < %d + %d",
+ len, number(HCIConstU8::EVENT_HDR_SIZE), paramSize);
+ continue; // discard data
+ }
+ std::shared_ptr<HCIEvent> event( HCIEvent::getSpecialized(rbuffer.get_ptr(), len) );
+ if( nullptr == event ) {
+ // not an event ...
+ ERR_PRINT("HCIHandler::reader: drop non-event %s", bytesHexString(rbuffer.get_ptr(), 0, len, true /* lsbFirst*/).c_str());
+ continue;
+ }
+ const HCIMetaEventType mec = event->getMetaEventType();
+ if( HCIMetaEventType::INVALID != mec && !filter_test_metaev(mec) ) {
+ // DROP
+ DBG_PRINT("HCIHandler::reader: drop %s", event->toString().c_str());
+ continue; // next packet
+ }
+#ifdef SHOW_LE_ADVERTISING
+ if( event->isMetaEvent(HCIMetaEventType::LE_ADVERTISING_REPORT) ) {
+ std::vector<std::shared_ptr<EInfoReport>> eirlist = EInfoReport::read_ad_reports(event->getParam(), event->getParamSize());
+ int i=0;
+ for_each_idx(eirlist, [&](std::shared_ptr<EInfoReport> &eir) {
+ INFO_PRINT("LE_ADV[%d]: %s", i, eir->toString().c_str());
+ i++;
+ });
+ continue; // next packet
+ }
+#endif /* SHOW_LE_ADVERTISING */
+ if( hciEventRing.isFull() ) {
+ std::shared_ptr<HCIEvent> ev = hciEventRing.get();
+ INFO_PRINT("HCIHandler::reader: full ring, dropping oldest %s",
+ ( nullptr != ev ) ? ev->toString().c_str() : "nil");
+ }
+ DBG_PRINT("HCIHandler::reader: got %s", event->toString().c_str());
+ hciEventRing.putBlocking( event );
+ } else if( ETIMEDOUT != errno && !hciReaderShallStop ) { // expected exits
+ ERR_PRINT("HCIHandler::reader: HCIComm error");
+ }
+ }
+ INFO_PRINT("HCIHandler::reader: Ended. Ring has %d entries flushed", hciEventRing.getSize());
+ hciReaderRunning = false;
+ hciEventRing.clear();
+}
+
+bool HCIHandler::sendCommand(HCICommand &req) {
+ const std::lock_guard<std::recursive_mutex> lock(comm.mutex()); // RAII-style acquire and relinquish via destructor
+ TROOctets & pdu = req.getPDU();
+ if ( comm.write( pdu.get_ptr(), pdu.getSize() ) < 0 ) {
+ ERR_PRINT("HCIHandler::sendWithReply: HCIComm write error, req %s", req.toString().c_str());
+ return false;
+ }
+ return true;
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::getNextReply(HCICommand &req, int & retryCount) {
+ // Ringbuffer read is thread safe
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = hciEventRing.getBlocking(replyTimeoutMS);
+ if( nullptr == ev ) {
+ errno = ETIMEDOUT;
+ DBG_PRINT("HCIHandler::getNextReply: nullptr result (timeout -> abort): req %s", req.toString().c_str());
+ return nullptr;
+ } else if( !ev->validate(req) ) {
+ // This could occur due to an earlier timeout w/ a nullptr == res (see above),
+ // i.e. the pending reply processed here and naturally not-matching.
+ retryCount++;
+ DBG_PRINT("HCIHandler::getNextReply: res mismatch (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ } else {
+ DBG_PRINT("HCIHandler::getNextReply: res: %s, req %s", ev->toString().c_str(), req.toString().c_str());
+ return ev;
+ }
+ }
+ return nullptr;
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithReply(HCICommand &req) {
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ return getNextReply(req, retryCount);
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithCmdCompleteReply(HCICommand &req, HCICommandCompleteEvent **res) {
+ *res = nullptr;
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+
+ int retryCount = 0;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = getNextReply(req, retryCount);
+ if( nullptr == ev ) {
+ return nullptr; // timeout
+ } else if( !ev->isEvent(HCIEventType::CMD_COMPLETE) ) {
+ DBG_PRINT("HCIHandler::sendWithCmdCompleteReply: !CMD_COMPLETE (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ continue; // next packet
+ } else {
+ *res = static_cast<HCICommandCompleteEvent*>(ev.get());
+ return ev;
+ }
+ }
+ return nullptr; // max retry
+}
+
+std::shared_ptr<HCIEvent> HCIHandler::sendWithCmdStatusReply(HCICommand &req, HCICommandStatusEvent **res) {
+ *res = nullptr;
+ if( !sendCommand(req) ) {
+ return nullptr;
+ }
+
+ int retryCount = 0;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ std::shared_ptr<HCIEvent> ev = getNextReply(req, retryCount);
+ if( nullptr == ev ) {
+ return nullptr; // timeout
+ } else if( !ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ DBG_PRINT("HCIHandler::sendWithCmdStatusReply: !CMD_STATUS (drop, retry %d): res %s; req %s",
+ retryCount, ev->toString().c_str(), req.toString().c_str());
+ continue; // next packet
+ } else {
+ *res = static_cast<HCICommandStatusEvent*>(ev.get());
+ return ev;
+ }
+ }
+ return nullptr; // max retry
+}
+
+HCIHandler::HCIHandler(const BTMode btMode, const uint16_t dev_id, const int replyTimeoutMS)
+:btMode(btMode), dev_id(dev_id), rbuffer(HCI_MAX_MTU),
+ comm(dev_id, HCI_CHANNEL_RAW, Defaults::HCI_READER_THREAD_POLL_TIMEOUT), replyTimeoutMS(replyTimeoutMS),
+ hciEventRing(HCI_EVT_RING_CAPACITY), hciReaderRunning(false), hciReaderShallStop(false)
+{
+ INFO_PRINT("HCIHandler.ctor: pid %d", HCIHandler::pidSelf);
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::open: Could not open hci control channel");
+ return;
+ }
+
+ std::thread hciReaderThread = std::thread(&HCIHandler::hciReaderThreadImpl, this);
+ hciReaderThreadId = hciReaderThread.native_handle();
+ // Avoid 'terminate called without an active exception'
+ // as l2capReaderThread may end due to I/O errors.
+ hciReaderThread.detach();
+
+ PERF_TS_T0();
+
+ // Mandatory socket filter (not adapter filter!)
+ {
+ hci_ufilter nf, of;
+ socklen_t olen;
+
+ olen = sizeof(of);
+ if (getsockopt(comm.dd(), SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+ ERR_PRINT("HCIHandler::ctor: getsockopt");
+ goto fail;
+ }
+ // uint16_t opcode_le16 = 0;
+ HCIComm::filter_clear(&nf);
+ HCIComm::filter_set_ptype(number(HCIPacketType::EVENT), &nf); // only EVENTs
+#if 0
+ HCIComm::filter_all_events(&nf); // all events
+#else
+ HCIComm::filter_set_event(number(HCIEventType::CONN_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::CMD_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::CMD_STATUS), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::HARDWARE_ERROR), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::LE_META), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_PHY_LINK_COMPLETE), &nf);
+ HCIComm::filter_set_event(number(HCIEventType::DISCONN_LOGICAL_LINK_COMPLETE), &nf);
+#endif
+ HCIComm::filter_set_opcode(0, &nf); // all opcode
+ if (setsockopt(comm.dd(), SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+ ERR_PRINT("HCIHandler::ctor: setsockopt");
+ goto fail;
+ }
+ }
+ // Mandatory own LE_META filter
+ {
+ filter_clear_metaevs();
+ // filter_all_metaevs();
+ filter_set_metaev(HCIMetaEventType::LE_CONN_COMPLETE);
+#ifdef SHOW_LE_ADVERTISING
+ filter_set_metaev(HCIMetaEventType::LE_ADVERTISING_REPORT);
+#endif
+ }
+ {
+ HCICommand req0(HCIOpcode::READ_LOCAL_VERSION, 0);
+ const hci_rp_read_local_version * ev_lv;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processCmdCompleteCommand<hci_rp_read_local_version>(
+ HCIOpcode::READ_LOCAL_VERSION, &ev_lv, &status);
+ if( nullptr == ev || nullptr == ev_lv ) {
+ ERR_PRINT("HCIHandler::ctor: failed READ_LOCAL_VERSION: 0x%x (%s)", number(status), getHCIErrorCodeString(status).c_str());
+ goto fail;
+ }
+ INFO_PRINT("HCIHandler::ctor: LOCAL_VERSION: %d.%d, manuf 0x%x, lmp %d.%d",
+ ev_lv->hci_ver, le_to_cpu(ev_lv->hci_rev), le_to_cpu(ev_lv->manufacturer),
+ ev_lv->lmp_ver, le_to_cpu(ev_lv->lmp_subver));
+ }
+
+ PERF_TS_TD("HCIHandler::open.ok");
+ return;
+
+fail:
+ close();
+ PERF_TS_TD("HCIHandler::open.fail");
+ return;
+}
+
+void HCIHandler::close() {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ DBG_PRINT("HCIHandler::close: Start");
+
+ const pthread_t tid_self = pthread_self();
+ const pthread_t tid_reader = hciReaderThreadId;
+ hciReaderThreadId = 0;
+ const bool is_reader = tid_reader == tid_self;
+ DBG_PRINT("HCIHandler.disconnect: Start hciReader[running %d, shallStop %d, isReader %d, tid %p)",
+ hciReaderRunning.load(), hciReaderShallStop.load(), is_reader, (void*)tid_reader);
+ if( hciReaderRunning ) {
+ hciReaderShallStop = true;
+ if( !is_reader && 0 != tid_reader ) {
+ int kerr;
+ if( 0 != ( kerr = pthread_kill(tid_reader, SIGALRM) ) ) {
+ ERR_PRINT("HCIHandler::disconnect: pthread_kill %p FAILED: %d", (void*)tid_reader, kerr);
+ }
+ }
+ }
+ comm.close();
+ DBG_PRINT("HCIHandler::close: End");
+}
+
+HCIErrorCode HCIHandler::reset() {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::reset: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ HCICommand req0(HCIOpcode::RESET, 0);
+ HCICommandCompleteEvent * ev_cc;
+ std::shared_ptr<HCIEvent> ev = sendWithCmdCompleteReply(req0, &ev_cc);
+ if( nullptr == ev || nullptr == ev_cc ) {
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ return ev_cc->getReturnStatus(0);
+}
+
+HCIErrorCode HCIHandler::le_create_conn(uint16_t * handle_return, const EUI48 &peer_bdaddr,
+ const HCIAddressType peer_mac_type,
+ const HCIAddressType own_mac_type,
+ const uint16_t le_scan_interval, const uint16_t le_scan_window,
+ const uint16_t conn_interval_min, const uint16_t conn_interval_max,
+ const uint16_t conn_latency, const uint16_t supervision_timeout) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( nullptr != handle_return ) {
+ *handle_return = 0;
+ }
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::le_create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ hci_cp_le_create_conn cp;
+
+ const uint16_t min_ce_length = 0x0000;
+ const uint16_t max_ce_length = 0x0000;
+ const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr*
+
+ bzero((void*)&cp, sizeof(cp));
+ cp.scan_interval = cpu_to_le(le_scan_interval);
+ cp.scan_window = cpu_to_le(le_scan_window);
+ cp.filter_policy = initiator_filter;
+ cp.peer_addr_type = peer_mac_type;
+ cp.peer_addr = peer_bdaddr;
+ cp.own_address_type = own_mac_type;
+ cp.conn_interval_min = cpu_to_le(conn_interval_min);
+ cp.conn_interval_max = cpu_to_le(conn_interval_max);
+ cp.conn_latency = cpu_to_le(conn_latency);
+ cp.supervision_timeout = cpu_to_le(supervision_timeout);
+ cp.min_ce_len = cpu_to_le(min_ce_length);
+ cp.max_ce_len = cpu_to_le(max_ce_length);
+
+ const hci_ev_le_conn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_le_create_conn, hci_ev_le_conn_complete>(
+ HCIOpcode::LE_CREATE_CONN, cp, HCIMetaEventType::LE_CONN_COMPLETE, &ev_cc, &status);
+
+ if( HCIErrorCode::SUCCESS != status ) {
+ return status;
+ }
+ if( nullptr != handle_return ) {
+ *handle_return = ev_cc->handle;
+ }
+ return HCIErrorCode::SUCCESS;
+}
+
+HCIErrorCode HCIHandler::create_conn(uint16_t * handle_return, const EUI48 &bdaddr,
+ const uint16_t pkt_type,
+ const uint16_t clock_offset, const uint8_t role_switch) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( nullptr != handle_return ) {
+ *handle_return = 0;
+ }
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ hci_cp_create_conn cp;
+
+ bzero((void*)&cp, sizeof(cp));
+ cp.bdaddr = bdaddr;
+ cp.pkt_type = cpu_to_le((uint16_t)(pkt_type & (uint16_t)ACL_PTYPE_MASK)); /* TODO OK excluding SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) ? */
+ cp.pscan_rep_mode = 0x02; /* TODO magic? */
+ cp.pscan_mode = 0x00; /* TODO magic? */
+ cp.clock_offset = cpu_to_le(clock_offset);
+ cp.role_switch = role_switch;
+
+ const hci_ev_conn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_create_conn, hci_ev_conn_complete>(
+ HCIOpcode::CREATE_CONN, cp, HCIEventType::CONN_COMPLETE, &ev_cc, &status);
+
+ if( HCIErrorCode::SUCCESS != status ) {
+ return status;
+ }
+ if( nullptr != handle_return ) {
+ *handle_return = ev_cc->handle;
+ }
+ return HCIErrorCode::SUCCESS;
+}
+
+HCIErrorCode HCIHandler::disconnect(const uint16_t conn_handle, const HCIErrorCode reason) {
+ const std::lock_guard<std::recursive_mutex> lock(mtx); // RAII-style acquire and relinquish via destructor
+ if( !comm.isOpen() ) {
+ ERR_PRINT("HCIHandler::create_conn: device not open");
+ return HCIErrorCode::INTERNAL_FAILURE;
+ }
+ if( 0 == conn_handle ) {
+ return HCIErrorCode::SUCCESS;
+ }
+ hci_cp_disconnect cp;
+
+ bzero(&cp, sizeof(cp));
+ cp.handle = conn_handle;
+ cp.reason = number(reason);
+
+ const hci_ev_disconn_complete * ev_cc;
+ HCIErrorCode status;
+ std::shared_ptr<HCIEvent> ev = processStructCommand<hci_cp_disconnect, hci_ev_disconn_complete>(
+ HCIOpcode::DISCONNECT, cp, HCIEventType::DISCONN_COMPLETE, &ev_cc, &status);
+ return status;
+}
+
+
+template<typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processCmdCompleteCommand(HCIOpcode opc, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ const HCIEventType evc = HCIEventType::CMD_COMPLETE;
+ HCICommand req0(opc, 0);
+ HCICommandCompleteEvent * ev_cc;
+ std::shared_ptr<HCIEvent> ev = sendWithCmdCompleteReply(req0, &ev_cc);
+ if( nullptr == ev ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr; // timeout
+ } else if( nullptr == ev_cc ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ const uint8_t returnParamSize = ev_cc->getReturnParamSize();
+ if( returnParamSize < sizeof(hci_cmd_event_struct) ) {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ *res = (const hci_cmd_event_struct*)(ev_cc->getReturnParam());
+ *status = static_cast<HCIErrorCode>((*res)->status);
+ DBG_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ return ev;
+}
+
+template<typename hci_command_struct, typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIEventType evc, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ HCIStructCommand<hci_command_struct> req0(opc, cp);
+ if( !sendCommand(req0) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ std::shared_ptr<HCIEvent> ev = nullptr;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ ev = getNextReply(req0, retryCount);
+ if( nullptr == ev ) {
+ break; // timeout, leave loop
+ } else if( ev->isEvent(evc) ) {
+ break; // gotcha, leave loop
+ } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ // pending command .. wait for result
+ HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
+ if( HCIErrorCode::SUCCESS != ev_cs->getStatus() ) {
+ *status = ev_cs->getStatus();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cs->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ continue; // next packet
+ } else {
+ continue; // next packet
+ }
+ }
+ if( nullptr == ev ) {
+ // timeout exit
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr;
+ }
+ typedef HCIStructCmdCompleteEvt<hci_cmd_event_struct> HCIConnCompleteEvt;
+ HCIConnCompleteEvt * ev_cc = static_cast<HCIConnCompleteEvt*>(ev.get());
+ if( ev_cc->isTypeAndSizeValid(evc) ) {
+ *status = ev_cc->getStatus();
+ *res = ev_cc->getStruct();
+ DBG_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ } else {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIEventTypeString(evc).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ }
+ return ev;
+}
+
+template<typename hci_command_struct, typename hci_cmd_event_struct>
+std::shared_ptr<HCIEvent> HCIHandler::processStructCommand(HCIOpcode opc, hci_command_struct &cp,
+ HCIMetaEventType mec, const hci_cmd_event_struct **res, HCIErrorCode *status)
+{
+ *res = nullptr;
+ *status = HCIErrorCode::INTERNAL_FAILURE;
+
+ HCIStructCommand<hci_command_struct> req0(opc, cp);
+ if( !sendCommand(req0) ) {
+ return nullptr;
+ }
+ int retryCount = 0;
+ std::shared_ptr<HCIEvent> ev = nullptr;
+
+ while( retryCount < HCI_READ_PACKET_MAX_RETRY ) {
+ ev = getNextReply(req0, retryCount);
+ if( nullptr == ev ) {
+ break; // timeout, leave loop
+ } else if( ev->isMetaEvent(mec) ) {
+ break; // gotcha, leave loop
+ } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
+ // pending command .. wait for result
+ HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
+ if( HCIErrorCode::SUCCESS != ev_cs->getStatus() ) {
+ *status = ev_cs->getStatus();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cs->toString().c_str(), req0.toString().c_str());
+ return ev;
+ }
+ continue; // next packet
+ } else {
+ continue; // next packet
+ }
+ }
+ if( nullptr == ev ) {
+ // timeout exit
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ req0.toString().c_str());
+ return nullptr;
+ }
+ typedef HCIStructCmdCompleteMetaEvt<hci_cmd_event_struct> HCIConnCompleteMetaEvt;
+ HCIConnCompleteMetaEvt * ev_cc = static_cast<HCIConnCompleteMetaEvt*>(ev.get());
+ if( ev_cc->isTypeAndSizeValid(mec) ) {
+ *status = ev_cc->getStatus();
+ *res = ev_cc->getStruct();
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Status 0x%2.2X (%s): res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ } else {
+ WARN_PRINT("HCIHandler::processStructCommand %s -> %s: Type or size mismatch: Status 0x%2.2X (%s), errno %d %s: res %s, req %s",
+ getHCIOpcodeString(opc).c_str(), getHCIMetaEventTypeString(mec).c_str(),
+ number(*status), getHCIErrorCodeString(*status).c_str(), errno, strerror(errno),
+ ev_cc->toString().c_str(), req0.toString().c_str());
+ }
+ return ev;
+}
diff --git a/src/direct_bt/HCITypes.cpp b/src/direct_bt/HCITypes.cpp
new file mode 100644
index 00000000..e5d066dd
--- /dev/null
+++ b/src/direct_bt/HCITypes.cpp
@@ -0,0 +1,299 @@
+/*
+ * 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 <cstring>
+#include <string>
+#include <memory>
+#include <cstdint>
+#include <vector>
+#include <cstdio>
+
+#include <algorithm>
+
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
+#include "HCITypes.hpp"
+
+extern "C" {
+ #include <inttypes.h>
+ #include <unistd.h>
+ #include <sys/param.h>
+ #include <sys/uio.h>
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+ #include <sys/socket.h>
+ #include <poll.h>
+}
+
+namespace direct_bt {
+
+#define HCI_ERROR_CODE(X) \
+ X(SUCCESS) \
+ X(UNKNOWN_HCI_COMMAND) \
+ X(UNKNOWN_CONNECTION_IDENTIFIER) \
+ X(HARDWARE_FAILURE) \
+ X(PAGE_TIMEOUT) \
+ X(AUTHENTICATION_FAILURE) \
+ X(PIN_OR_KEY_MISSING) \
+ X(MEMORY_CAPACITY_EXCEEDED) \
+ X(CONNECTION_TIMEOUT) \
+ X(CONNECTION_LIMIT_EXCEEDED) \
+ X(SYNC_DEVICE_CONNECTION_LIMIT_EXCEEDED) \
+ X(CONNECTION_ALREADY_EXISTS) \
+ X(COMMAND_DISALLOWED) \
+ X(CONNECTION_REJECTED_LIMITED_RESOURCES) \
+ X(CONNECTION_REJECTED_SECURITY) \
+ X(CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR) \
+ X(CONNECTION_ACCEPT_TIMEOUT_EXCEEDED) \
+ X(UNSUPPORTED_FEATURE_OR_PARAM_VALUE) \
+ X(INVALID_HCI_COMMAND_PARAMETERS) \
+ X(REMOTE_USER_TERMINATED_CONNECTION) \
+ X(REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES) \
+ X(REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF) \
+ X(CONNECTION_TERMINATED_BY_LOCAL_HOST) \
+ X(REPEATED_ATTEMPTS) \
+ X(PAIRING_NOT_ALLOWED) \
+ X(UNKNOWN_LMP_PDU) \
+ X(UNSUPPORTED_REMOTE_OR_LMP_FEATURE) \
+ X(SCO_OFFSET_REJECTED) \
+ X(SCO_INTERVAL_REJECTED) \
+ X(SCO_AIR_MODE_REJECTED) \
+ X(INVALID_LMP_OR_LL_PARAMETERS) \
+ X(UNSPECIFIED_ERROR) \
+ X(UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE) \
+ X(ROLE_CHANGE_NOT_ALLOWED) \
+ X(LMP_OR_LL_RESPONSE_TIMEOUT) \
+ X(LMP_OR_LL_COLLISION) \
+ X(LMP_PDU_NOT_ALLOWED) \
+ X(ENCRYPTION_MODE_NOT_ACCEPTED) \
+ X(LINK_KEY_CANNOT_BE_CHANGED) \
+ X(REQUESTED_QOS_NOT_SUPPORTED) \
+ X(INSTANT_PASSED) \
+ X(PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) \
+ X(DIFFERENT_TRANSACTION_COLLISION) \
+ X(QOS_UNACCEPTABLE_PARAMETER) \
+ X(QOS_REJECTED) \
+ X(CHANNEL_ASSESSMENT_NOT_SUPPORTED) \
+ X(INSUFFICIENT_SECURITY) \
+ X(PARAMETER_OUT_OF_RANGE) \
+ X(ROLE_SWITCH_PENDING) \
+ X(RESERVED_SLOT_VIOLATION) \
+ X(ROLE_SWITCH_FAILED) \
+ X(EIR_TOO_LARGE) \
+ X(SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST) \
+ X(HOST_BUSY_PAIRING) \
+ X(CONNECTION_REJECTED_NO_SUITABLE_CHANNEL) \
+ X(CONTROLLER_BUSY) \
+ X(UNACCEPTABLE_CONNECTION_PARAM) \
+ X(ADVERTISING_TIMEOUT) \
+ X(CONNECTION_TERMINATED_MIC_FAILURE) \
+ X(CONNECTION_EST_FAILED_OR_SYNC_TIMETOUT) \
+ X(MAX_CONNECTION_FAILED) \
+ X(COARSE_CLOCK_ADJ_REJECTED) \
+ X(TYPE0_SUBMAP_NOT_DEFINED) \
+ X(UNKNOWN_ADVERTISING_IDENTIFIER) \
+ X(LIMIT_REACHED) \
+ X(OPERATION_CANCELLED_BY_HOST) \
+ X(PACKET_TOO_LONG) \
+ X(INTERNAL_FAILURE) \
+ X(UNKNOWN)
+
+#define HCI_ERROR_CODE_CASE_TO_STRING(V) case HCIErrorCode::V: return #V;
+
+std::string getHCIErrorCodeString(const HCIErrorCode ec) {
+ switch(ec) {
+ HCI_ERROR_CODE(HCI_ERROR_CODE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIErrorCode";
+}
+
+std::string getHCIPacketTypeString(const HCIPacketType op) {
+ switch(op) {
+ case HCIPacketType::COMMAND: return "COMMAND";
+ case HCIPacketType::ACLDATA: return "ACLDATA";
+ case HCIPacketType::SCODATA: return "SCODATA";
+ case HCIPacketType::EVENT: return "EVENT";
+ case HCIPacketType::DIAG: return "DIAG";
+ case HCIPacketType::VENDOR: return "VENDOR";
+ }
+ return "Unknown HCIPacketType";
+}
+
+std::string getHCIOGFString(const HCIOGF op) {
+ (void)op;
+ return "";
+}
+
+#define HCI_OPCODE(X) \
+ X(SPECIAL) \
+ X(CREATE_CONN) \
+ X(DISCONNECT) \
+ X(SET_EVENT_MASK) \
+ X(RESET) \
+ X(READ_LOCAL_VERSION) \
+ X(LE_SET_EVENT_MASK) \
+ X(LE_READ_BUFFER_SIZE) \
+ X(LE_READ_LOCAL_FEATURES) \
+ X(LE_SET_RANDOM_ADDR) \
+ X(LE_SET_ADV_PARAM) \
+ X(LE_READ_ADV_TX_POWER) \
+ X(LE_SET_ADV_DATA) \
+ X(LE_SET_SCAN_RSP_DATA) \
+ X(LE_SET_ADV_ENABLE) \
+ X(LE_SET_SCAN_PARAM) \
+ X(LE_SET_SCAN_ENABLE) \
+ X(LE_CREATE_CONN) \
+ X(LE_CREATE_CONN_CANCEL) \
+ X(LE_READ_WHITE_LIST_SIZE) \
+ X(LE_CLEAR_WHITE_LIST) \
+ X(LE_ADD_TO_WHITE_LIST) \
+ X(LE_DEL_FROM_WHITE_LIST) \
+ X(LE_CONN_UPDATE) \
+ X(LE_READ_REMOTE_FEATURES) \
+ X(LE_START_ENC)
+
+#define HCI_OPCODE_CASE_TO_STRING(V) case HCIOpcode::V: return #V;
+
+std::string getHCIOpcodeString(const HCIOpcode op) {
+ switch(op) {
+ HCI_OPCODE(HCI_OPCODE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIOpcode";
+}
+
+#define HCI_EVENTTYPE(X) \
+ X(INVALID) \
+ X(INQUIRY_COMPLETE) \
+ X(INQUIRY_RESULT) \
+ X(CONN_COMPLETE) \
+ X(CONN_REQUEST) \
+ X(DISCONN_COMPLETE) \
+ X(AUTH_COMPLETE) \
+ X(REMOTE_NAME) \
+ X(ENCRYPT_CHANGE) \
+ X(CHANGE_LINK_KEY_COMPLETE) \
+ X(REMOTE_FEATURES) \
+ X(REMOTE_VERSION) \
+ X(QOS_SETUP_COMPLETE) \
+ X(CMD_COMPLETE) \
+ X(CMD_STATUS) \
+ X(HARDWARE_ERROR) \
+ X(ROLE_CHANGE) \
+ X(NUM_COMP_PKTS) \
+ X(MODE_CHANGE) \
+ X(PIN_CODE_REQ) \
+ X(LINK_KEY_REQ) \
+ X(LINK_KEY_NOTIFY) \
+ X(CLOCK_OFFSET) \
+ X(PKT_TYPE_CHANGE) \
+ X(DISCONN_PHY_LINK_COMPLETE) \
+ X(DISCONN_LOGICAL_LINK_COMPLETE) \
+ X(LE_META)
+
+#define HCI_EVENTTYPE_CASE_TO_STRING(V) case HCIEventType::V: return #V;
+
+std::string getHCIEventTypeString(const HCIEventType op) {
+ switch(op) {
+ HCI_EVENTTYPE(HCI_EVENTTYPE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIEventType";
+}
+
+#define HCI_METATYPE(X) \
+ X(INVALID) \
+ X(LE_CONN_COMPLETE) \
+ X(LE_ADVERTISING_REPORT) \
+ X(LE_CONN_UPDATE_COMPLETE) \
+ X(LE_REMOTE_FEAT_COMPLETE) \
+ X(LE_LTKEY_REQUEST) \
+ X(LE_REMOTE_CONN_PARAM_REQ) \
+ X(LE_DATA_LENGTH_CHANGE) \
+ X(LE_READ_LOCAL_P256_PUBKEY_COMPLETE) \
+ X(LE_GENERATE_DHKEY_COMPLETE) \
+ X(LE_ENHANCED_CONN_COMPLETE) \
+ X(LE_DIRECT_ADV_REPORT) \
+ X(LE_PHY_UPDATE_COMPLETE) \
+ X(LE_EXT_ADV_REPORT) \
+ X(LE_PERIODIC_ADV_SYNC_ESTABLISHED) \
+ X(LE_PERIODIC_ADV_REPORT) \
+ X(LE_PERIODIC_ADV_SYNC_LOST) \
+ X(LE_SCAN_TIMEOUT) \
+ X(LE_ADV_SET_TERMINATED) \
+ X(LE_SCAN_REQ_RECEIVED) \
+ X(LE_CHANNEL_SEL_ALGO) \
+ X(LE_CONNLESS_IQ_REPORT) \
+ X(LE_CONN_IQ_REPORT) \
+ X(LE_CTE_REQ_FAILED) \
+ X(LE_PERIODIC_ADV_SYNC_TRANSFER_RECV) \
+ X(LE_CIS_ESTABLISHED) \
+ X(LE_CIS_REQUEST) \
+ X(LE_CREATE_BIG_COMPLETE) \
+ X(LE_TERMINATE_BIG_COMPLETE) \
+ X(LE_BIG_SYNC_ESTABLISHED) \
+ X(LE_BIG_SYNC_LOST) \
+ X(LE_REQUEST_PEER_SCA_COMPLETE) \
+ X(LE_PATH_LOSS_THRESHOLD) \
+ X(LE_TRANSMIT_POWER_REPORTING) \
+ X(LE_BIGINFO_ADV_REPORT)
+
+#define HCI_METATYPE_CASE_TO_STRING(V) case HCIMetaEventType::V: return #V;
+
+std::string getHCIMetaEventTypeString(const HCIMetaEventType op) {
+ switch(op) {
+ HCI_METATYPE(HCI_METATYPE_CASE_TO_STRING)
+ default: ; // fall through intended
+ }
+ return "Unknown HCIMetaType";
+}
+
+HCIEvent* HCIEvent::getSpecialized(const uint8_t * buffer, int const buffer_size) {
+ const HCIPacketType pc = static_cast<HCIPacketType>( get_uint8(buffer, 0) );
+ if( HCIPacketType::EVENT != pc ) {
+ return nullptr;
+ }
+ const HCIEventType ec = static_cast<HCIEventType>( get_uint8(buffer, 1) );
+
+ switch( ec ) {
+ case HCIEventType::DISCONN_COMPLETE:
+ return new HCIDisconnectionCompleteEvent(buffer, buffer_size);
+ case HCIEventType::CMD_COMPLETE:
+ return new HCICommandCompleteEvent(buffer, buffer_size);
+ case HCIEventType::CMD_STATUS:
+ return new HCICommandStatusEvent(buffer, buffer_size);
+ case HCIEventType::LE_META:
+ // No need to HCIMetaType specializations as we use HCIStructCmdCompleteMetaEvt template
+ // based on HCIMetaEvent.
+ return new HCIMetaEvent(buffer, buffer_size);
+ default:
+ // No further specialization, use HCIStructCmdCompleteEvt template
+ return new HCIEvent(buffer, buffer_size);
+ }
+}
+
+} /* namespace direct_bt */