Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
L2CAPComm.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 // #define PERF_PRINT_ON 1
36 #include <jau/debug.hpp>
37 
38 #include "BTIoctl.hpp"
39 #include "HCIIoctl.hpp"
40 #include "L2CAPIoctl.hpp"
41 
42 #include "HCIComm.hpp"
43 #include "L2CAPComm.hpp"
44 
45 #include "BTAdapter.hpp"
46 
47 extern "C" {
48  #include <unistd.h>
49  #include <sys/socket.h>
50  #include <poll.h>
51  #include <signal.h>
52 }
53 
54 using namespace direct_bt;
55 
56 L2CAPEnv::L2CAPEnv() noexcept
57 : exploding( jau::environment::getExplodingProperties("direct_bt.l2cap") ),
58  L2CAP_READER_POLL_TIMEOUT( jau::environment::getInt32Property("direct_bt.l2cap.reader.timeout", 10000, 1500 /* min */, INT32_MAX /* max */) ),
59  L2CAP_RESTART_COUNT_ON_ERROR( jau::environment::getInt32Property("direct_bt.l2cap.restart.count", 5, INT32_MIN /* min */, INT32_MAX /* max */) ), // FIXME: Move to L2CAPComm
60  DEBUG_DATA( jau::environment::getBooleanProperty("direct_bt.debug.l2cap.data", false) )
61 {
62 }
63 
64 int L2CAPComm::l2cap_open_dev(const BDAddressAndType & adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) {
65  sockaddr_l2 a;
66  int fd, err;
67 
68  // Create a loose L2CAP socket
69  fd = ::socket(AF_BLUETOOTH, // AF_BLUETOOTH == PF_BLUETOOTH
70  SOCK_SEQPACKET, BTPROTO_L2CAP);
71 
72  if( 0 > fd ) {
73  ERR_PRINT("L2CAPComm::l2cap_open_dev: socket failed");
74  return fd;
75  }
76 
77  // Bind socket to the L2CAP adapter
78  // BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ
79  bzero((void *)&a, sizeof(a));
80  a.l2_family=AF_BLUETOOTH;
81  a.l2_psm = jau::cpu_to_le(direct_bt::number(psm));
82  a.l2_bdaddr = adapterAddressAndType.address;
83  a.l2_cid = jau::cpu_to_le(direct_bt::number(cid));
84  a.l2_bdaddr_type = ::number(adapterAddressAndType.type);
85  if ( ::bind(fd, (struct sockaddr *) &a, sizeof(a)) < 0 ) {
86  ERR_PRINT("L2CAPComm::l2cap_open_dev: bind failed");
87  goto failed;
88  }
89  return fd;
90 
91 failed:
92  err = errno;
93  ::close(fd);
94  errno = err;
95 
96  return -1;
97 }
98 
99 int L2CAPComm::l2cap_close_dev(int dd)
100 {
101  return ::close(dd);
102 }
103 
104 
105 // *************************************************
106 // *************************************************
107 // *************************************************
108 
109 L2CAPComm::L2CAPComm(const BDAddressAndType& adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_)
110 : env(L2CAPEnv::get()),
111  adapterAddressAndType(adapterAddressAndType_),
112  psm(psm_), cid(cid_),
113  deviceAddressAndType(BDAddressAndType::ANY_BREDR_DEVICE),
114  socket_descriptor(-1),
115  is_open(false), has_ioerror(false), interrupt_flag(false), tid_connect(0), tid_read(0)
116 { }
117 
118 
119 /**
120  * Setting BT_SECURITY within open() after bind() and before connect()
121  * causes BlueZ/Kernel to immediately process SMP, leading to a potential deadlock.
122  *
123  * Here we experience, setting security level before connect()
124  * will block the thread within connect(), potentially a mutex used in the SMP procedure.
125  *
126  * Hence we set BT_SECURITY after connect() within open().
127  */
128 #define SET_BT_SECURITY_POST_CONNECT 1
129 
130 bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) {
131 
132  bool expOpen = false; // C++11, exp as value since C++20
133  if( !is_open.compare_exchange_strong(expOpen, true) ) {
134  DBG_PRINT("L2CAPComm::open: Already open: %s, dd %d, %s, psm %s, cid %s",
135  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
136  to_string(psm).c_str(), to_string(cid).c_str());
137  return false;
138  }
139  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
140 
141  /**
142  * bt_io_connect ( create_io ) with source address
143  * - fd = socket(.._)
144  * - bind(fd, ..)
145  * - l2cap_set
146  * -- set imtu, omtu, mode
147  * -- l2cap_set_master
148  * -- l2cap_set_flushable
149  * -- set_priority
150  * -- set_sec_level
151  * --- setsockopt(.. BT_SECURITY ..)
152  *
153  * - l2cap_connect with destination address
154  * -- connect(fd, ..)
155  */
156  deviceAddressAndType = device.getAddressAndType();
157 
158  /** BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ */
159  sockaddr_l2 req;
160  int res;
161  int to_retry_count=0; // ETIMEDOUT retry count
162 
163  DBG_PRINT("L2CAPComm::open: Start Connect: %s, dd %d, %s, psm %s, cid %s",
164  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
165  to_string(psm).c_str(), to_string(cid).c_str());
166 
167  socket_descriptor = l2cap_open_dev(adapterAddressAndType, psm, cid);
168 
169  if( 0 > socket_descriptor ) {
170  goto failure; // open failed
171  }
172 
173 #if !SET_BT_SECURITY_POST_CONNECT
174  #if USE_LINUX_BT_SECURITY
175  if( BTSecurityLevel::UNSET < sec_level ) {
176  if( !setBTSecurityLevelImpl(sec_level) ) {
177  goto failure; // sec_level failed
178  }
179  }
180  #endif
181 #endif
182 
183  tid_connect = pthread_self(); // temporary safe tid to allow interruption
184 
185  // actual request to connect to remote device
186  bzero((void *)&req, sizeof(req));
187  req.l2_family = AF_BLUETOOTH;
188  req.l2_psm = jau::cpu_to_le(direct_bt::number(psm));
189  req.l2_bdaddr = deviceAddressAndType.address;
190  req.l2_cid = jau::cpu_to_le(direct_bt::number(cid));
191  req.l2_bdaddr_type = ::number(deviceAddressAndType.type);
192 
193  while( !interrupt_flag ) {
194  // blocking
195  res = ::connect(socket_descriptor, (struct sockaddr*)&req, sizeof(req));
196 
197  DBG_PRINT("L2CAPComm::open: Connect Result %d, errno 0%X %s, %s", res, errno, strerror(errno), deviceAddressAndType.toString().c_str());
198 
199  if( !res )
200  {
201  break; // done
202 
203  } else if( ETIMEDOUT == errno ) {
204  to_retry_count++;
205  if( to_retry_count < number(Defaults::L2CAP_CONNECT_MAX_RETRY) ) {
206  WORDY_PRINT("L2CAPComm::open: Connect timeout, retry %d", to_retry_count);
207  continue;
208  } else {
209  ERR_PRINT("L2CAPComm::open: Connect timeout, retried %d", to_retry_count);
210  goto failure; // exit
211  }
212 
213  } else {
214  // EALREADY == errno || ENETUNREACH == errno || EHOSTUNREACH == errno || ..
215  ERR_PRINT("L2CAPComm::open: Connect failed");
216  goto failure; // exit
217  }
218  }
219  // success
220  tid_connect = 0;
221 
222 #if SET_BT_SECURITY_POST_CONNECT
223  #if USE_LINUX_BT_SECURITY
224  if( BTSecurityLevel::UNSET < sec_level ) {
225  if( !setBTSecurityLevelImpl(sec_level) ) {
226  goto failure; // sec_level failed
227  }
228  }
229  #endif
230 #endif
231 
232  return true;
233 
234 failure:
235  const int err = errno;
236  close();
237  errno = err;
238  return false;
239 }
240 
241 bool L2CAPComm::close() noexcept {
242  bool expOpen = true; // C++11, exp as value since C++20
243  if( !is_open.compare_exchange_strong(expOpen, false) ) {
244  DBG_PRINT("L2CAPComm::close: Not connected: %s, dd %d, %s, psm %s, cid %s",
245  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
246  to_string(psm).c_str(), to_string(cid).c_str());
247  return true;
248  }
249  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
250 
251  has_ioerror = false;
252  DBG_PRINT("L2CAPComm::close: Start: %s, dd %d, %s, psm %u, cid %u",
253  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(), psm, cid);
254  PERF_TS_T0();
255 
256  interrupt_flag = true;
257  {
258  pthread_t tid_self = pthread_self();
259  pthread_t _tid_connect = tid_connect;
260  pthread_t _tid_read = tid_read;
261  tid_read = 0;
262  tid_connect = 0;
263 
264  // interrupt read(..) and , avoiding prolonged hang
265  if( 0 != _tid_read && tid_self != _tid_read ) {
266  int kerr;
267  if( 0 != ( kerr = pthread_kill(_tid_read, SIGALRM) ) ) {
268  ERR_PRINT("L2CAPComm::close: pthread_kill read %p FAILED: %d", (void*)_tid_read, kerr);
269  }
270  }
271  // interrupt connect(..) and , avoiding prolonged hang
272  interrupt_flag = true;
273  if( 0 != _tid_connect && _tid_read != _tid_connect && tid_self != _tid_connect ) {
274  int kerr;
275  if( 0 != ( kerr = pthread_kill(_tid_connect, SIGALRM) ) ) {
276  ERR_PRINT("L2CAPComm::close: pthread_kill connect %p FAILED: %d", (void*)_tid_connect, kerr);
277  }
278  }
279  }
280 
281  l2cap_close_dev(socket_descriptor);
282  socket_descriptor = -1;
283  interrupt_flag = false;
284  PERF_TS_TD("L2CAPComm::close");
285  DBG_PRINT("L2CAPComm::close: End: dd %d", socket_descriptor.load());
286  return true;
287 }
288 
290  if( !is_open ) {
291  DBG_PRINT("L2CAPComm::setBTSecurityLevel: Not connected: %s, dd %d, %s, psm %s, cid %s",
292  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
293  to_string(psm).c_str(), to_string(cid).c_str());
294  return false;
295  }
296  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
297 
298  return setBTSecurityLevelImpl(sec_level);
299 }
300 
301 bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) {
302  if( BTSecurityLevel::NONE > sec_level ) {
303  DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not set", to_string(sec_level).c_str());
304  return false;
305  }
306 
307 #if USE_LINUX_BT_SECURITY
308  struct bt_security bt_sec;
309  int result;
310 
311  BTSecurityLevel old_sec_level = getBTSecurityLevelImpl();
312  if( old_sec_level != sec_level ) {
313  bzero(&bt_sec, sizeof(bt_sec));
314  bt_sec.level = direct_bt::number(sec_level);
315  result = setsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec));
316  if ( 0 == result ) {
317  DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s -> %s, success",
318  to_string(old_sec_level).c_str(), to_string(sec_level).c_str());
319  return true;
320  } else {
321  ERR_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s -> %s, failed",
322  to_string(old_sec_level).c_str(), to_string(sec_level).c_str());
323  return false;
324  }
325  } else {
326  DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s == %s, success (ignored)",
327  to_string(old_sec_level).c_str(), to_string(sec_level).c_str());
328  return true;
329  }
330 #else
331  DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not implemented", to_string(sec_level).c_str());
332  return false;
333 #endif
334 }
335 
337  if( !is_open ) {
338  DBG_PRINT("L2CAPComm::getBTSecurityLevel: Not connected: %s, dd %d, %s, psm %s, cid %s",
339  getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
340  to_string(psm).c_str(), to_string(cid).c_str());
341  return BTSecurityLevel::UNSET;
342  }
343  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
344 
345  return getBTSecurityLevelImpl();
346 }
347 
348 BTSecurityLevel L2CAPComm::getBTSecurityLevelImpl() {
350 #if USE_LINUX_BT_SECURITY
351  struct bt_security bt_sec;
352  socklen_t optlen = sizeof(bt_sec);
353  int result;
354 
355  bzero(&bt_sec, sizeof(bt_sec));
356  result = getsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, &optlen);
357  if ( 0 == result ) {
358  if( optlen == sizeof(bt_sec) ) {
359  sec_level = static_cast<BTSecurityLevel>(bt_sec.level);
360  DBG_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, success", to_string(sec_level).c_str());
361  } else {
362  ERR_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, failed. Returned size %zd != %zd ",
363  to_string(sec_level).c_str(), optlen, sizeof(bt_sec));
364  }
365  } else {
366  ERR_PRINT("L2CAPComm::getBTSecurityLevel: sec_level %s, failed. Result %d", to_string(sec_level).c_str(), result);
367  }
368 #else
369  DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s, not implemented", to_string(sec_level).c_str());
370 #endif
371  return sec_level;
372 }
373 
374 jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) {
375  const int32_t timeoutMS = env.L2CAP_READER_POLL_TIMEOUT;
376  jau::snsize_t len = 0;
377  jau::snsize_t err_res = 0;
378 
379  tid_read = pthread_self(); // temporary safe tid to allow interruption
380 
381  if( 0 > socket_descriptor ) {
382  err_res = -1; // invalid socket_descriptor or capacity
383  goto errout;
384  }
385  if( 0 == capacity ) {
386  goto done;
387  }
388 
389  if( timeoutMS ) {
390  struct pollfd p;
391  int n;
392 
393  p.fd = socket_descriptor; p.events = POLLIN;
394  while ( !interrupt_flag && (n = poll(&p, 1, timeoutMS)) < 0 ) {
395  if ( !interrupt_flag && ( errno == EAGAIN || errno == EINTR ) ) {
396  // cont temp unavail or interruption
397  continue;
398  }
399  err_res = -10; // poll error !(ETIMEDOUT || EAGAIN || EINTR)
400  goto errout;
401  }
402  if (!n) {
403  err_res = -11; // poll error ETIMEDOUT
404  errno = ETIMEDOUT;
405  goto errout;
406  }
407  }
408 
409  while ((len = ::read(socket_descriptor, buffer, capacity)) < 0) {
410  if (errno == EAGAIN || errno == EINTR ) {
411  // cont temp unavail or interruption
412  continue;
413  }
414  err_res = -20; // read error
415  goto errout;
416  }
417 
418 done:
419  tid_read = 0;
420  return len;
421 
422 errout:
423  tid_read = 0;
424  if( errno != ETIMEDOUT ) {
425  has_ioerror = true;
426  if( is_open ) {
427  if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) {
428  ABORT("L2CAPComm::read: Error res %d; %s, dd %d, %s, psm %s, cid %s",
429  err_res, getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
430  to_string(psm).c_str(), to_string(cid).c_str());
431  } else {
432  IRQ_PRINT("L2CAPComm::read: Error res %d; %s, dd %d, %s, psm %s, cid %s",
433  err_res, getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
434  to_string(psm).c_str(), to_string(cid).c_str());
435  }
436  }
437  }
438  return err_res;
439 }
440 
441 jau::snsize_t L2CAPComm::write(const uint8_t * buffer, const jau::nsize_t length) {
442  const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
443  jau::snsize_t len = 0;
444  jau::snsize_t err_res = 0;
445 
446  if( 0 > socket_descriptor ) {
447  err_res = -1; // invalid socket_descriptor or capacity
448  goto errout;
449  }
450  if( 0 == length ) {
451  goto done;
452  }
453 
454  while( ( len = ::write(socket_descriptor, buffer, length) ) < 0 ) {
455  if( EAGAIN == errno || EINTR == errno ) {
456  continue;
457  }
458  err_res = -10; // write error !(EAGAIN || EINTR)
459  goto errout;
460  }
461 
462 done:
463  return len;
464 
465 errout:
466  has_ioerror = true;
467  if( is_open ) {
468  if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) {
469  ABORT("L2CAPComm::write: Error res %d; %s, dd %d, %s, psm %s, cid %s",
470  err_res, getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
471  to_string(psm).c_str(), to_string(cid).c_str());
472  } else {
473  IRQ_PRINT("L2CAPComm::write: Error res %d; %s, dd %d, %s, psm %s, cid %s",
474  err_res, getStateString().c_str(), socket_descriptor.load(), deviceAddressAndType.toString().c_str(),
475  to_string(psm).c_str(), to_string(cid).c_str());
476  }
477  }
478  return err_res;
479 }
480 
direct_bt::L2CAPComm::number
static constexpr int number(const Defaults d)
Definition: L2CAPComm.hpp:118
L2CAPComm.hpp
IRQ_PRINT
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE: '.
Definition: debug.hpp:138
direct_bt::L2CAP_CID
L2CAP_CID
Definition: BTTypes0.hpp:432
direct_bt::L2CAPEnv
L2CAP Singleton runtime environment properties.
Definition: L2CAPComm.hpp:59
direct_bt
Definition: ATTPDUTypes.hpp:171
direct_bt::L2CAPComm::getBTSecurityLevel
BTSecurityLevel getBTSecurityLevel()
Fetches the current BlueZ's L2CAP socket BT_SECURITY sec_level.
Definition: L2CAPComm.cpp:336
direct_bt::L2CAPEnv::L2CAP_READER_POLL_TIMEOUT
const int32_t L2CAP_READER_POLL_TIMEOUT
L2CAP poll timeout for reading, defaults to 10s.
Definition: L2CAPComm.hpp:72
direct_bt::BDAddressAndType::toString
std::string toString() const noexcept
Definition: BTTypes0.cpp:333
jau
Definition: basic_algos.hpp:34
direct_bt::BTSecurityLevel::NONE
@ NONE
No encryption and no authentication.
direct_bt::L2CAPComm::Defaults::L2CAP_CONNECT_MAX_RETRY
@ L2CAP_CONNECT_MAX_RETRY
direct_bt::BTDevice::getAddressAndType
constexpr BDAddressAndType const & getAddressAndType() const noexcept
Returns the unique device EUI48 address and BDAddressType type.
Definition: BTDevice.hpp:278
direct_bt::BTSecurityLevel
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:211
direct_bt::L2CAPComm::getStateString
std::string getStateString() const
Definition: L2CAPComm.hpp:178
ABORT
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ FILE:LINE: '...
Definition: debug.hpp:124
BTAdapter.hpp
direct_bt::BDAddressAndType::type
BDAddressType type
Definition: BTAddress.hpp:429
direct_bt::number
constexpr uint8_t number(const BDAddressType rhs) noexcept
Definition: BTAddress.hpp:67
direct_bt::L2CAPComm::L2CAPComm
L2CAPComm(const BDAddressAndType &adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid)
Constructing a non connected L2CAP channel instance for the pre-defined PSM and CID.
Definition: L2CAPComm.cpp:109
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::BTSecurityRegistry::get
Entry * get(const EUI48 &addr, const std::string &name, AddressNameEntryMatchFunc m) noexcept
Returns a matching BTSecurityRegistry::Entry with the given.
Definition: BTSecurityRegistry.cpp:36
direct_bt::BTSecurityLevel::UNSET
@ UNSET
Security Level not set, value 0.
direct_bt::L2CAP_PSM
L2CAP_PSM
Protocol Service Multiplexers (PSM) Assigned numbers https://www.bluetooth.com/specifications/assigne...
Definition: BTTypes0.hpp:458
jau::cpu_to_le
constexpr uint16_t cpu_to_le(uint16_t const h) noexcept
Definition: byte_util.hpp:334
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
direct_bt::L2CAPComm::open
bool open(const BTDevice &device, const BTSecurityLevel sec_level=BTSecurityLevel::NONE)
Opens and connects the L2CAP channel, locking mutex_write().
Definition: L2CAPComm.cpp:130
debug.hpp
direct_bt::to_string
std::string to_string(const BDAddressType type) noexcept
Definition: BTTypes0.cpp:129
direct_bt::BDAddressAndType::address
EUI48 address
Definition: BTAddress.hpp:428
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
ERR_PRINT
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '.
Definition: debug.hpp:132
direct_bt::L2CAPEnv::L2CAP_RESTART_COUNT_ON_ERROR
const int32_t L2CAP_RESTART_COUNT_ON_ERROR
Debugging facility: L2CAP restart count on transmission errors, defaults to 5 attempts.
Definition: L2CAPComm.hpp:83
HCIComm.hpp
direct_bt::L2CAPComm::setBTSecurityLevel
bool setBTSecurityLevel(const BTSecurityLevel sec_level)
If sec_level > BTSecurityLevel::UNSET, sets the BlueZ's L2CAP socket BT_SECURITY sec_level,...
Definition: L2CAPComm.cpp:289
WORDY_PRINT
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:91
PERF_TS_TD
#define PERF_TS_TD(m)
Definition: debug.hpp:103
direct_bt::BDAddressAndType
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:417
direct_bt::BTDevice
Definition: BTDevice.hpp:57
direct_bt::L2CAPComm::close
bool close() noexcept
Closing the L2CAP channel, locking mutex_write().
Definition: L2CAPComm.cpp:241
DBG_PRINT
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:78
PERF_TS_T0
#define PERF_TS_T0()
Definition: debug.hpp:102