Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
SMPHandler.cpp
Go to the documentation of this file.
1 /*
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  * Copyright (c) 2020 ZAFENA AB
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <cstring>
27 #include <string>
28 #include <memory>
29 #include <cstdint>
30 #include <vector>
31 #include <cstdio>
32 
33 #include <algorithm>
34 
35 extern "C" {
36  #include <unistd.h>
37  #include <sys/socket.h>
38  #include <poll.h>
39  #include <signal.h>
40 }
41 
42 // #define PERF_PRINT_ON 1
43 // PERF2_PRINT_ON for read/write single values
44 // #define PERF2_PRINT_ON 1
45 // PERF3_PRINT_ON for disconnect
46 // #define PERF3_PRINT_ON 1
47 #include <jau/debug.hpp>
48 
49 #include <jau/basic_algos.hpp>
50 
51 #include "L2CAPIoctl.hpp"
52 
53 #include "SMPHandler.hpp"
54 
55 #include "BTDevice.hpp"
56 #include "BTAdapter.hpp"
57 
58 using namespace direct_bt;
59 
60 SMPEnv::SMPEnv() noexcept
61 : exploding( jau::environment::getExplodingProperties("direct_bt.smp") ),
62  SMP_READ_COMMAND_REPLY_TIMEOUT( jau::environment::getInt32Property("direct_bt.smp.cmd.read.timeout", 500, 250 /* min */, INT32_MAX /* max */) ),
63  SMP_WRITE_COMMAND_REPLY_TIMEOUT( jau::environment::getInt32Property("direct_bt.smp.cmd.write.timeout", 500, 250 /* min */, INT32_MAX /* max */) ),
64  SMPPDU_RING_CAPACITY( jau::environment::getInt32Property("direct_bt.smp.ringsize", 128, 64 /* min */, 1024 /* max */) ),
65  DEBUG_DATA( jau::environment::getBooleanProperty("direct_bt.debug.smp.data", false) )
66 {
67 }
68 
69 #if SMP_SUPPORTED_BY_OS
71 #else
72  bool SMPHandler::IS_SUPPORTED_BY_OS = false;
73 #endif
74 
75 std::shared_ptr<BTDevice> SMPHandler::getDeviceChecked() const {
76  std::shared_ptr<BTDevice> ref = wbr_device.lock();
77  if( nullptr == ref ) {
78  throw jau::IllegalStateException("SMPHandler's device already destructed: "+deviceString, E_FILE_LINE);
79  }
80  return ref;
81 }
82 
83 bool SMPHandler::validateConnected() noexcept {
84  bool l2capIsConnected = l2cap.isOpen();
85  bool l2capHasIOError = l2cap.hasIOError();
86 
87  if( has_ioerror || l2capHasIOError ) {
88  has_ioerror = true; // propagate l2capHasIOError -> has_ioerror
89  ERR_PRINT("IOError state: GattHandler %s, l2cap %s: %s",
90  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
91  return false;
92  }
93 
94  if( !is_connected || !l2capIsConnected ) {
95  ERR_PRINT("Disconnected state: GattHandler %s, l2cap %s: %s",
96  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
97  return false;
98  }
99  return true;
100 }
101 
102 void SMPHandler::l2capReaderThreadImpl() {
103  {
104  const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
105  l2capReaderShallStop = false;
106  l2capReaderRunning = true;
107  DBG_PRINT("SMPHandler::reader Started");
108  cv_l2capReaderInit.notify_all();
109  }
110  thread_local jau::call_on_release thread_cleanup([&]() {
111  DBG_PRINT("SMPHandler::l2capReaderThreadCleanup: l2capReaderRunning %d -> 0", l2capReaderRunning.load());
112  l2capReaderRunning = false;
113  });
114 
115  while( !l2capReaderShallStop ) {
116  jau::snsize_t len;
117  if( !validateConnected() ) {
118  ERR_PRINT("SMPHandler::reader: Invalid IO state -> Stop");
119  l2capReaderShallStop = true;
120  break;
121  }
122 
123  len = l2cap.read(rbuffer.get_wptr(), rbuffer.getSize());
124  if( 0 < len ) {
125  std::unique_ptr<const SMPPDUMsg> smpPDU = SMPPDUMsg::getSpecialized(rbuffer.get_ptr(), static_cast<jau::nsize_t>(len));
126  const SMPPDUMsg::Opcode opc = smpPDU->getOpcode();
127 
129  COND_PRINT(env.DEBUG_DATA, "SMPHandler-IO RECV (SEC_REQ) %s", smpPDU->toString().c_str());
130  jau::for_each_fidelity(smpSecurityReqCallbackList, [&](SMPSecurityReqCallback &cb) {
131  cb.invoke(*smpPDU);
132  });
133  } else {
134  COND_PRINT(env.DEBUG_DATA, "SMPHandler-IO RECV (MSG) %s", smpPDU->toString().c_str());
135  if( smpPDURing.isFull() ) {
136  const jau::nsize_t dropCount = smpPDURing.capacity()/4;
137  smpPDURing.drop(dropCount);
138  WARN_PRINT("SMPHandler-IO RECV Drop (%u oldest elements of %u capacity, ring full)", dropCount, smpPDURing.capacity());
139  }
140  smpPDURing.putBlocking( std::move(smpPDU) );
141  }
142  } else if( ETIMEDOUT != errno && !l2capReaderShallStop ) { // expected exits
143  IRQ_PRINT("SMPHandler::reader: l2cap read error -> Stop; l2cap.read %d", len);
144  l2capReaderShallStop = true;
145  has_ioerror = true;
146  }
147  }
148  {
149  const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
150  WORDY_PRINT("SMPHandler::reader: Ended. Ring has %u entries flushed", smpPDURing.getSize());
151  smpPDURing.clear();
152  l2capReaderRunning = false;
153  cv_l2capReaderInit.notify_all();
154  }
155  disconnect(true /* disconnectDevice */, has_ioerror);
156 }
157 
158 SMPHandler::SMPHandler(const std::shared_ptr<BTDevice> &device) noexcept
159 : env(SMPEnv::get()),
160  wbr_device(device), deviceString(device->getAddressAndType().toString()), rbuffer(number(Defaults::SMP_MTU_BUFFER_SZ)),
161  l2cap(device->getAdapter().getAddressAndType(), L2CAP_PSM::UNDEFINED, L2CAP_CID::SMP),
162  is_connected(l2cap.open(*device)), has_ioerror(false),
163  smpPDURing(nullptr, env.SMPPDU_RING_CAPACITY), l2capReaderShallStop(false),
164  l2capReaderThreadId(0), l2capReaderRunning(false),
165  mtu(number(Defaults::MIN_SMP_MTU))
166 {
167  if( !validateConnected() ) {
168  ERR_PRINT("SMPHandler.ctor: L2CAP could not connect");
169  is_connected = false;
170  return;
171  }
172  DBG_PRINT("SMPHandler::ctor: Start Connect: GattHandler[%s], l2cap[%s]: %s",
173  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
174 
175  /**
176  * We utilize DBTManager's mgmthandler_sigaction SIGALRM handler,
177  * as we only can install one handler.
178  */
179  {
180  std::unique_lock<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
181 
182  std::thread l2capReaderThread(&SMPHandler::l2capReaderThreadImpl, this); // @suppress("Invalid arguments")
183  l2capReaderThreadId = l2capReaderThread.native_handle();
184  // Avoid 'terminate called without an active exception'
185  // as l2capReaderThread may end due to I/O errors.
186  l2capReaderThread.detach();
187 
188  while( false == l2capReaderRunning ) {
189  cv_l2capReaderInit.wait(lock);
190  }
191  }
192 
193  // FIXME: Determine proper MTU usage: Defaults::MIN_SMP_MTU or Defaults::LE_SECURE_SMP_MTU (if enabled)
194  uint16_t mtu_ = number(Defaults::MIN_SMP_MTU);
195  mtu = std::min(number(Defaults::LE_SECURE_SMP_MTU), (int)mtu_);
196 }
197 
199  disconnect(false /* disconnectDevice */, false /* ioErrorCause */);
200  clearAllCallbacks();
201 }
202 
204  // FIXME: Start negotiating security!
205  // FIXME: Return true only if security has been established (encryption and optionally authentication)
206  (void)sec_level;
207  return false;
208 }
209 
210 bool SMPHandler::disconnect(const bool disconnectDevice, const bool ioErrorCause) noexcept {
211  PERF3_TS_T0();
212  // Interrupt SM's L2CAP::connect(..) and L2CAP::read(..), avoiding prolonged hang
213  // and pull all underlying l2cap read operations!
214  l2cap.close();
215 
216  // Avoid disconnect re-entry -> potential deadlock
217  bool expConn = true; // C++11, exp as value since C++20
218  if( !is_connected.compare_exchange_strong(expConn, false) ) {
219  // not connected
220  DBG_PRINT("SMPHandler::disconnect: Not connected: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
221  disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
222  clearAllCallbacks();
223  return false;
224  }
225  // Lock to avoid other threads using instance while disconnecting
226  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
227  DBG_PRINT("SMPHandler::disconnect: Start: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
228  disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
229  clearAllCallbacks();
230 
231  PERF3_TS_TD("SMPHandler::disconnect.1");
232  {
233  std::unique_lock<std::mutex> lockReader(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
234  has_ioerror = false;
235 
236  const pthread_t tid_self = pthread_self();
237  const pthread_t tid_l2capReader = l2capReaderThreadId;
238  l2capReaderThreadId = 0;
239  const bool is_l2capReader = tid_l2capReader == tid_self;
240  DBG_PRINT("SMPHandler.disconnect: l2capReader[running %d, shallStop %d, isReader %d, tid %p)",
241  l2capReaderRunning.load(), l2capReaderShallStop.load(), is_l2capReader, (void*)tid_l2capReader);
242  if( l2capReaderRunning ) {
243  l2capReaderShallStop = true;
244  if( !is_l2capReader && 0 != tid_l2capReader ) {
245  int kerr;
246  if( 0 != ( kerr = pthread_kill(tid_l2capReader, SIGALRM) ) ) {
247  ERR_PRINT("SMPHandler::disconnect: pthread_kill %p FAILED: %d", (void*)tid_l2capReader, kerr);
248  }
249  }
250  // Ensure the reader thread has ended, no runaway-thread using *this instance after destruction
251  while( true == l2capReaderRunning ) {
252  cv_l2capReaderInit.wait(lockReader);
253  }
254  }
255  }
256  PERF3_TS_TD("SMPHandler::disconnect.2");
257 
258  if( disconnectDevice ) {
259  std::shared_ptr<BTDevice> device = getDeviceUnchecked();
260  if( nullptr != device ) {
261  // Cleanup device resources, proper connection state
262  // Intentionally giving the POWER_OFF reason for the device in case of ioErrorCause!
263  const HCIStatusCode reason = ioErrorCause ?
266  device->disconnect(reason);
267  }
268  }
269 
270  PERF3_TS_TD("SMPHandler::disconnect.X");
271  DBG_PRINT("SMPHandler::disconnect: End: %s", deviceString.c_str());
272  return true;
273 }
274 
275 void SMPHandler::send(const SMPPDUMsg & msg) {
276  if( !validateConnected() ) {
277  throw jau::IllegalStateException("SMPHandler::send: Invalid IO State: req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
278  }
279  if( msg.pdu.getSize() > mtu ) {
280  throw jau::IllegalArgumentException("clientMaxMTU "+std::to_string(msg.pdu.getSize())+" > usedMTU "+std::to_string(mtu)+
281  " to "+deviceString, E_FILE_LINE);
282  }
283 
284  // Thread safe l2cap.write(..) operation..
285  const ssize_t res = l2cap.write(msg.pdu.get_ptr(), msg.pdu.getSize());
286  if( 0 > res ) {
287  IRQ_PRINT("SMPHandler::send: l2cap write error -> disconnect: %s to %s", msg.toString().c_str(), deviceString.c_str());
288  has_ioerror = true;
289  disconnect(true /* disconnectDevice */, true /* ioErrorCause */); // state -> Disconnected
290  throw BTException("SMPHandler::send: l2cap write error: req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
291  }
292  if( static_cast<size_t>(res) != msg.pdu.getSize() ) {
293  ERR_PRINT("SMPHandler::send: l2cap write count error, %zd != %zu: %s -> disconnect: %s",
294  res, msg.pdu.getSize(), msg.toString().c_str(), deviceString.c_str());
295  has_ioerror = true;
296  disconnect(true /* disconnectDevice */, true /* ioErrorCause */); // state -> Disconnected
297  throw BTException("SMPHandler::send: l2cap write count error, "+std::to_string(res)+" != "+std::to_string(res)
298  +": "+msg.toString()+" -> disconnect: "+deviceString, E_FILE_LINE);
299  }
300 }
301 
302 std::unique_ptr<const SMPPDUMsg> SMPHandler::sendWithReply(const SMPPDUMsg & msg, const int timeout) {
303  send( msg );
304 
305  // Ringbuffer read is thread safe
306  std::unique_ptr<const SMPPDUMsg> res = smpPDURing.getBlocking(timeout);
307  if( nullptr == res ) {
308  errno = ETIMEDOUT;
309  IRQ_PRINT("SMPHandler::sendWithReply: nullptr result (timeout %d): req %s to %s", timeout, msg.toString().c_str(), deviceString.c_str());
310  has_ioerror = true;
311  disconnect(true /* disconnectDevice */, true /* ioErrorCause */);
312  throw BTException("SMPHandler::sendWithReply: nullptr result (timeout "+std::to_string(timeout)+"): req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
313  }
314  return res;
315 }
316 
317 /**
318  * SMPSecurityReqCallback handling
319  */
320 
322  [](const SMPSecurityReqCallback& a, const SMPSecurityReqCallback& b) -> bool { return a == b; };
323 
324 
326  smpSecurityReqCallbackList.push_back(l);
327 }
329  return smpSecurityReqCallbackList.erase_matching(l, true /* all_matching */, _changedSMPSecurityReqCallbackEqComp);
330 }
331 
332 void SMPHandler::clearAllCallbacks() noexcept {
333  smpSecurityReqCallbackList.clear();
334 }
335 
direct_bt::SMPHandler::disconnect
bool disconnect(const bool disconnectDevice, const bool ioErrorCause) noexcept
Disconnect this GATTHandler and optionally the associated device.
Definition: SMPHandler.cpp:210
IRQ_PRINT
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE: '.
Definition: debug.hpp:138
direct_bt::L2CAPComm::getStateString
static std::string getStateString(bool isConnected, bool hasIOError)
Definition: L2CAPComm.hpp:120
direct_bt::SMPHandler::addSMPSecurityReqCallback
void addSMPSecurityReqCallback(const SMPSecurityReqCallback &l)
Definition: SMPHandler.cpp:325
direct_bt::TROOctets::getSize
constexpr jau::nsize_t getSize() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: OctetTypes.hpp:118
jau::for_each_fidelity
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
Definition: basic_algos.hpp:215
direct_bt::SMPHandler::getStateString
std::string getStateString() const noexcept
Definition: SMPHandler.hpp:236
direct_bt::SMPHandler::removeSMPSecurityReqCallback
int removeSMPSecurityReqCallback(const SMPSecurityReqCallback &l)
Definition: SMPHandler.cpp:328
direct_bt::TOctets::get_wptr
uint8_t * get_wptr() noexcept
Definition: OctetTypes.hpp:383
jau::cow_darray::erase_matching
constexpr_atomic int erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Definition: cow_darray.hpp:986
direct_bt::HCIStatusCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
@ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
jau::call_on_release
Call on release allows the user to pass a function to be called at destruction of this instance.
Definition: basic_algos.hpp:58
basic_algos.hpp
direct_bt::SMPHandler::~SMPHandler
~SMPHandler() noexcept
Destructor closing this instance including L2CAP channel, see disconnect().
Definition: SMPHandler.cpp:198
jau::IllegalArgumentException
Definition: basic_types.hpp:111
direct_bt
Definition: ATTPDUTypes.hpp:171
PERF3_TS_TD
#define PERF3_TS_TD(m)
Definition: debug.hpp:117
direct_bt::SMPPDUMsg::Opcode::SECURITY_REQUEST
@ SECURITY_REQUEST
direct_bt::L2CAP_CID::SMP
@ SMP
direct_bt::TROOctets::get_ptr
constexpr uint8_t const * get_ptr() const noexcept
Definition: OctetTypes.hpp:228
jau
Definition: basic_algos.hpp:34
PERF3_TS_T0
#define PERF3_TS_T0()
Definition: debug.hpp:116
direct_bt::SMPHandler::IS_SUPPORTED_BY_OS
static bool IS_SUPPORTED_BY_OS
Linux/BlueZ prohibits access to the existing SMP implementation via L2CAP (socket).
Definition: SMPHandler.hpp:174
direct_bt::BTException
Definition: BTTypes0.hpp:43
jau::to_string
PRAGMA_DISABLE_WARNING_POP constexpr_cxx20 std::string to_string(const endian &v) noexcept
Return std::string representation of the given jau::endian.
Definition: byte_util.hpp:198
direct_bt::BTSecurityLevel
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:211
jau::FunctionDef
Definition: function_def.hpp:309
direct_bt::HCIStatusCode
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:124
E_FILE_LINE
#define E_FILE_LINE
Definition: basic_types.hpp:64
SMPHandler.hpp
_changedSMPSecurityReqCallbackEqComp
static SMPSecurityReqCallbackList::equal_comparator _changedSMPSecurityReqCallbackEqComp
SMPSecurityReqCallback handling.
Definition: SMPHandler.cpp:321
BTAdapter.hpp
direct_bt::L2CAPComm::hasIOError
bool hasIOError() const
Definition: L2CAPComm.hpp:177
WARN_PRINT
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE: '.
Definition: debug.hpp:146
jau::ordered_atomic::load
CXX_ALWAYS_INLINE _Tp load() const noexcept
Definition: ordered_atomic.hpp:139
direct_bt::BTDevice::disconnect
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect the LE or BREDR peer's GATT and HCI connection.
Definition: BTDevice.cpp:1701
direct_bt::number
constexpr uint8_t number(const BDAddressType rhs) noexcept
Definition: BTAddress.hpp:67
direct_bt::SMPPDUMsg
Handles the Security Manager Protocol (SMP) using Protocol Data Unit (PDU) encoded messages over L2CA...
Definition: SMPTypes.hpp:644
direct_bt::SMPPDUMsg::Opcode
Opcode
SMP Command Codes Vol 3, Part H (SM): 3.3.
Definition: SMPTypes.hpp:647
direct_bt::L2CAPComm::read
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity)
Generic read, w/o locking suitable for a unique ringbuffer sink.
Definition: L2CAPComm.cpp:374
direct_bt::SMPPDUMsg::toString
virtual std::string toString() const noexcept
Definition: SMPTypes.hpp:795
direct_bt::L2CAPComm::write
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length)
Generic write, locking mutex_write().
Definition: L2CAPComm.cpp:441
debug.hpp
direct_bt::SMPPDUMsg::pdu
POctets pdu
actual received PDU
Definition: SMPTypes.hpp:703
jau::IllegalStateException
Definition: basic_types.hpp:117
direct_bt::SMPEnv::get
static SMPEnv & get() noexcept
Definition: SMPHandler.hpp:136
jau::snsize_t
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:56
jau::nsize_t
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:44
jau::cow_darray< SMPSecurityReqCallback >::equal_comparator
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
Definition: cow_darray.hpp:921
jau::cow_darray::clear
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:751
ERR_PRINT
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '.
Definition: debug.hpp:132
direct_bt::SMPHandler::getDeviceChecked
std::shared_ptr< BTDevice > getDeviceChecked() const
Definition: SMPHandler.cpp:75
jau::FunctionDef::invoke
R invoke(A... args)
Definition: function_def.hpp:354
direct_bt::SMPHandler::SMPHandler
SMPHandler(const std::shared_ptr< BTDevice > &device) noexcept
Definition: SMPHandler.cpp:158
direct_bt::L2CAPComm::isOpen
bool isOpen() const
Definition: L2CAPComm.hpp:169
direct_bt::SMPPDUMsg::getOpcode
constexpr Opcode getOpcode() const noexcept
SMP Command Codes Vol 3, Part H (SM): 3.3.
Definition: SMPTypes.hpp:752
direct_bt::SMPEnv::DEBUG_DATA
const bool DEBUG_DATA
Debug all SMP Data communication.
Definition: SMPHandler.hpp:133
jau::cow_darray::push_back
constexpr_atomic void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: cow_darray.hpp:810
direct_bt::SMPHandler::establishSecurity
bool establishSecurity(const BTSecurityLevel sec_level)
If sec_level > BTSecurityLevel::UNSET, change security level per L2CAP connection.
Definition: SMPHandler.cpp:203
WORDY_PRINT
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:91
direct_bt::HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION
@ REMOTE_USER_TERMINATED_CONNECTION
direct_bt::SMPPDUMsg::getSpecialized
static std::unique_ptr< const SMPPDUMsg > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
Definition: SMPTypes.cpp:423
direct_bt::L2CAP_PSM::UNDEFINED
@ UNDEFINED
BTDevice.hpp
COND_PRINT
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:165
DBG_PRINT
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:78