Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
HCIHandler.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 <cstdio>
31
32#include <algorithm>
33
34// #define PERF_PRINT_ON 1
35#include <jau/debug.hpp>
36
37#include <jau/environment.hpp>
38#include <jau/basic_algos.hpp>
40
41#include "BTIoctl.hpp"
42
43#include "HCIIoctl.hpp"
44#include "HCIComm.hpp"
45#include "HCIHandler.hpp"
46#include "BTTypes1.hpp"
47#include "SMPHandler.hpp"
48#include "DBTConst.hpp"
49
50extern "C" {
51 #include <inttypes.h>
52 #include <unistd.h>
53 #include <poll.h>
54 #include <signal.h>
55 #ifdef __linux__
56 #include <sys/ioctl.h>
57 #endif
58}
59
60using namespace direct_bt;
61using namespace jau::fractions_i64_literals;
62
63HCIEnv::HCIEnv() noexcept
64: exploding( jau::environment::getExplodingProperties("direct_bt.hci") ),
65 HCI_READER_THREAD_POLL_TIMEOUT( jau::environment::getFractionProperty("direct_bt.hci.reader.timeout", 10_s, 1500_ms /* min */, 365_d /* max */) ),
66 HCI_COMMAND_STATUS_REPLY_TIMEOUT( jau::environment::getFractionProperty("direct_bt.hci.cmd.status.timeout", 3_s, 1500_ms /* min */, 365_d /* max */) ),
67 HCI_COMMAND_COMPLETE_REPLY_TIMEOUT( jau::environment::getFractionProperty("direct_bt.hci.cmd.complete.timeout", 10_s, 1500_ms /* min */, 365_d /* max */) ),
68 HCI_COMMAND_POLL_PERIOD( jau::environment::getFractionProperty("direct_bt.hci.cmd.poll.period", 125_ms, 50_ms, 365_d) ),
69 HCI_EVT_RING_CAPACITY( jau::environment::getInt32Property("direct_bt.hci.ringsize", 64, 64 /* min */, 1024 /* max */) ),
70 DEBUG_EVENT( jau::environment::getBooleanProperty("direct_bt.debug.hci.event", false) ),
71 DEBUG_SCAN_AD_EIR( jau::environment::getBooleanProperty("direct_bt.debug.hci.scan_ad_eir", false) ),
72 HCI_READ_PACKET_MAX_RETRY( HCI_EVT_RING_CAPACITY )
73{
74}
75
76__pack( struct hci_rp_status {
77 __u8 status;
78} );
79
80HCIHandler::HCIConnectionRef HCIHandler::setResolvHCIConnectionAddr(jau::darray<HCIConnectionRef> &list,
81 const BDAddressAndType& visibleAddressAndType,
82 const BDAddressAndType& addressAndType) noexcept {
83 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
84 auto end = list.end();
85 for (auto it = list.begin(); it != end; ++it) {
86 HCIConnectionRef conn = *it;
87 if ( conn->equals(visibleAddressAndType) ) {
88 conn->setResolvAddrAndType(addressAndType);
89 return conn; // done
90 }
91 }
92 return nullptr;
93}
94
95HCIHandler::HCIConnectionRef HCIHandler::addOrUpdateHCIConnection(jau::darray<HCIConnectionRef> &list,
96 const BDAddressAndType& addressAndType, const uint16_t handle) noexcept {
97 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
98 auto end = list.end();
99 for (auto it = list.begin(); it != end; ++it) {
100 HCIConnectionRef conn = *it;
101 if ( conn->equals(addressAndType) ) {
102 // reuse same entry
103 WORDY_PRINT("HCIHandler<%u>::addTrackerConnection: address%s, handle %s: reuse entry %s - %s",
104 dev_id, addressAndType.toString().c_str(), jau::to_hexstring(handle).c_str(),
105 conn->toString().c_str(), toString().c_str());
106 // Overwrite tracked connection handle with given _valid_ handle only, i.e. non zero!
107 if( 0 != handle ) {
108 if( 0 != conn->getHandle() && handle != conn->getHandle() ) {
109 WARN_PRINT("address%s, handle %s: reusing entry %s, overwriting non-zero handle - %s",
110 addressAndType.toString().c_str(), jau::to_hexstring(handle).c_str(),
111 conn->toString().c_str(), toString().c_str());
112 }
113 conn->setHandle( handle );
114 }
115 return conn; // done
116 }
117 }
118 try {
119 HCIConnectionRef res( std::make_shared<HCIConnection>(addressAndType, handle) );
120 list.push_back( res );
121 return res;
122 } catch (const std::bad_alloc &e) {
123 ABORT("Error: bad_alloc: HCIConnectionRef allocation failed");
124 return nullptr; // unreachable
125 }
126}
127
128HCIHandler::HCIConnectionRef HCIHandler::findHCIConnection(jau::darray<HCIConnectionRef> &list, const BDAddressAndType& addressAndType) noexcept {
129 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
130 const jau::nsize_t size = list.size();
131 for (jau::nsize_t i = 0; i < size; i++) {
132 HCIConnectionRef & e = list[i];
133 if( e->equals(addressAndType) ) {
134 return e;
135 }
136 }
137 return nullptr;
138}
139
140HCIHandler::HCIConnectionRef HCIHandler::findTrackerConnection(const uint16_t handle) noexcept {
141 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
142 const jau::nsize_t size = connectionList.size();
143 for (jau::nsize_t i = 0; i < size; i++) {
144 HCIConnectionRef & e = connectionList[i];
145 if ( handle == e->getHandle() ) {
146 return e;
147 }
148 }
149 return nullptr;
150}
151
152HCIHandler::HCIConnectionRef HCIHandler::removeTrackerConnection(const HCIConnectionRef& conn) noexcept {
153 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
154 auto end = connectionList.end();
155 for (auto it = connectionList.begin(); it != end; ++it) {
156 HCIConnectionRef e = *it;
157 if ( *e == *conn ) {
158 connectionList.erase(it);
159 return e; // done
160 }
161 }
162 return nullptr;
163}
164HCIHandler::size_type HCIHandler::countPendingTrackerConnections() noexcept {
165 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
166 size_type count = 0;
167 for (const auto& e : connectionList) {
168 if ( e->getHandle() == 0 ) {
169 count++;
170 }
171 }
172 return count;
173}
174HCIHandler::size_type HCIHandler::getTrackerConnectionCount() noexcept {
175 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
176 return connectionList.size();
177}
178HCIHandler::HCIConnectionRef HCIHandler::removeHCIConnection(jau::darray<HCIConnectionRef> &list, const uint16_t handle) noexcept {
179 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
180 auto end = list.end();
181 for (auto it = list.begin(); it != end; ++it) {
182 HCIConnectionRef e = *it;
183 if ( e->getHandle() == handle ) {
184 list.erase(it);
185 return e; // done
186 }
187 }
188 return nullptr;
189}
190
191MgmtEvent::Opcode HCIHandler::translate(HCIEventType evt, HCIMetaEventType met) noexcept {
192 if( HCIEventType::LE_META == evt ) {
193 switch( met ) {
195 [[fallthrough]];
198
199 default:
201 }
202 }
203 switch( evt ) {
208 default: return MgmtEvent::Opcode::INVALID;
209 }
210}
211
212std::unique_ptr<MgmtEvent> HCIHandler::translate(HCIEvent& ev) noexcept {
213 const HCIEventType evt = ev.getEventType();
214 const HCIMetaEventType mevt = ev.getMetaEventType();
215
216 if( HCIEventType::LE_META == evt ) {
217 switch( mevt ) {
219 HCIStatusCode status;
220 const hci_ev_le_conn_complete * ev_cc = getMetaReplyStruct<hci_ev_le_conn_complete>(ev, mevt, &status);
221 if( nullptr == ev_cc ) {
222 ERR_PRINT("LE_CONN_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
223 return nullptr;
224 }
225 const HCILEPeerAddressType hciAddrType = static_cast<HCILEPeerAddressType>(ev_cc->bdaddr_type);
226 const BDAddressAndType addressAndType(jau::le_to_cpu(ev_cc->bdaddr), to_BDAddressType(hciAddrType));
227 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
228 const HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, handle);
229 if( HCIStatusCode::SUCCESS == status ) {
230 advertisingEnabled = false;
231 return std::make_unique<MgmtEvtDeviceConnected>(dev_id, addressAndType, handle);
232 } else {
233 removeTrackerConnection(conn);
234 return std::make_unique<MgmtEvtDeviceConnectFailed>(dev_id, addressAndType, status);
235 }
236 }
238 const HCILELTKReqEvent & ev2 = *static_cast<const HCILELTKReqEvent*>( &ev );
239 const HCIConnectionRef conn = findTrackerConnection(ev2.getHandle());
240 if( nullptr == conn ) {
241 WARN_PRINT("dev_id %u: LE_LTK_REQUEST: Not tracked conn_handle of %s", dev_id, ev2.toString().c_str());
242 return nullptr;
243 }
244 return std::make_unique<MgmtEvtHCILELTKReq>(dev_id, conn->getAddressAndType(), ev2.getRand(), ev2.getEDIV());
245 }
247 HCIStatusCode status;
248 const hci_ev_le_enh_conn_complete * ev_cc = getMetaReplyStruct<hci_ev_le_enh_conn_complete>(ev, mevt, &status);
249 if( nullptr == ev_cc ) {
250 ERR_PRINT("LE_EXT_CONN_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
251 return nullptr;
252 }
253 const HCILEPeerAddressType hciAddrType = static_cast<HCILEPeerAddressType>(ev_cc->bdaddr_type);
254 const BDAddressAndType addressAndType(jau::le_to_cpu(ev_cc->bdaddr), to_BDAddressType(hciAddrType));
255 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
256 const HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, handle);
257 if( HCIStatusCode::SUCCESS == status ) {
258 advertisingEnabled = false;
259 return std::make_unique<MgmtEvtDeviceConnected>(dev_id, addressAndType, handle);
260 } else {
261 removeTrackerConnection(conn);
262 return std::make_unique<MgmtEvtDeviceConnectFailed>(dev_id, addressAndType, status);
263 }
264 }
266 HCIStatusCode status;
267 const hci_ev_le_remote_feat_complete * ev_cc = getMetaReplyStruct<hci_ev_le_remote_feat_complete>(ev, mevt, &status);
268 if( nullptr == ev_cc ) {
269 ERR_PRINT("LE_REMOTE_FEAT_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
270 return nullptr;
271 }
272 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
273 const LE_Features features = static_cast<LE_Features>(jau::get_uint64(ev_cc->features + 0, jau::lb_endian_t::little));
274 const HCIConnectionRef conn = findTrackerConnection(handle);
275 if( nullptr == conn ) {
276 WARN_PRINT("dev_id %u:: LE_REMOTE_FEAT_COMPLETE: Not tracked conn_handle %s of %s",
277 dev_id, jau::to_hexstring(handle).c_str(), ev.toString().c_str());
278 return nullptr;
279 }
280 return std::make_unique<MgmtEvtHCILERemoteFeatures>(dev_id, conn->getAddressAndType(), status, features);
281 }
283 HCIStatusCode status;
284 struct le_phy_update_complete {
285 uint8_t status;
286 uint16_t handle;
287 uint8_t tx;
288 uint8_t rx;
289 } __packed;
290 const le_phy_update_complete * ev_cc = getMetaReplyStruct<le_phy_update_complete>(ev, mevt, &status);
291 if( nullptr == ev_cc ) {
292 ERR_PRINT("LE_PHY_UPDATE_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
293 return nullptr;
294 }
295 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
296 const LE_PHYs Tx = static_cast<LE_PHYs>(ev_cc->tx);
297 const LE_PHYs Rx = static_cast<LE_PHYs>(ev_cc->rx);
298 const HCIConnectionRef conn = findTrackerConnection(handle);
299 if( nullptr == conn ) {
300 WARN_PRINT("dev_id %u:: LE_PHY_UPDATE_COMPLETE: Not tracked conn_handle %s of %s",
301 dev_id, jau::to_hexstring(handle).c_str(), ev.toString().c_str());
302 return nullptr;
303 }
304 return std::make_unique<MgmtEvtHCILEPhyUpdateComplete>(dev_id, conn->getAddressAndType(), status, Tx, Rx);
305 }
306 default:
307 return nullptr;
308 }
309 }
310 switch( evt ) {
312 HCIStatusCode status;
313 const hci_ev_conn_complete * ev_cc = getReplyStruct<hci_ev_conn_complete>(ev, evt, &status);
314 if( nullptr == ev_cc ) {
315 ERR_PRINT("CONN_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
316 return nullptr;
317 }
318 const BDAddressAndType addressAndType(jau::le_to_cpu(ev_cc->bdaddr), BDAddressType::BDADDR_BREDR);
319 HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, ev_cc->handle);
320 if( HCIStatusCode::SUCCESS == status ) {
321 advertisingEnabled = false;
322 return std::make_unique<MgmtEvtDeviceConnected>(dev_id, conn->getAddressAndType(), conn->getHandle());
323 } else {
324 try {
325 std::unique_ptr<MgmtEvent> res( std::make_unique<MgmtEvtDeviceConnectFailed>(dev_id, conn->getAddressAndType(),status) );
326 removeTrackerConnection(conn);
327 return res;
328 } catch (const std::bad_alloc &e) {
329 ABORT("Error: bad_alloc: MgmtEvtDeviceConnectFailedRef allocation failed");
330 return nullptr; // unreachable
331 }
332 }
333 }
335 HCIStatusCode status;
336 const hci_ev_disconn_complete * ev_cc = getReplyStruct<hci_ev_disconn_complete>(ev, evt, &status);
337 if( nullptr == ev_cc ) {
338 ERR_PRINT("DISCONN_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
339 return nullptr;
340 }
341 removeDisconnectCmd(ev_cc->handle);
342 HCIConnectionRef conn = removeTrackerConnection(ev_cc->handle);
343 if( nullptr == conn ) {
344 WORDY_PRINT("HCIHandler<%u>::translate(evt): DISCONN_COMPLETE: Not tracked handle %s: %s of %s",
345 dev_id, jau::to_hexstring(ev_cc->handle).c_str(), ev.toString().c_str(), toString().c_str());
346 return nullptr;
347 } else {
348 if( HCIStatusCode::SUCCESS != status ) {
349 // FIXME: Ever occuring? Still sending out essential disconnect event!
350 ERR_PRINT("DISCONN_COMPLETE: !SUCCESS[%s, %s], %s: %s - %s",
351 jau::to_hexstring(static_cast<uint8_t>(status)).c_str(), to_string(status).c_str(),
352 conn->toString().c_str(), ev.toString().c_str(), toString().c_str());
353 }
354 const HCIStatusCode hciRootReason = static_cast<HCIStatusCode>(ev_cc->reason);
355 return std::make_unique<MgmtEvtDeviceDisconnected>(dev_id, conn->getAddressAndType(), hciRootReason, conn->getHandle());
356 }
357 }
359 HCIStatusCode status;
360 const hci_ev_encrypt_change * ev_cc = getReplyStruct<hci_ev_encrypt_change>(ev, evt, &status);
361 if( nullptr == ev_cc ) {
362 ERR_PRINT("ENCRYPT_CHANGE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
363 return nullptr;
364 }
365 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
366 const HCIConnectionRef conn = findTrackerConnection(handle);
367 if( nullptr == conn ) {
368 WARN_PRINT("dev_id %u:: ENCRYPT_CHANGE: Not tracked conn_handle %s of %s",
369 dev_id, jau::to_hexstring(handle).c_str(), ev.toString().c_str());
370 return nullptr;
371 }
372 return std::make_unique<MgmtEvtHCIEncryptionChanged>(dev_id, conn->getAddressAndType(), status, ev_cc->encrypt);
373 }
375 HCIStatusCode status;
376 const hci_ev_key_refresh_complete * ev_cc = getReplyStruct<hci_ev_key_refresh_complete>(ev, evt, &status);
377 if( nullptr == ev_cc ) {
378 ERR_PRINT("ENCRYPT_KEY_REFRESH_COMPLETE: Null reply-struct: %s - %s", ev.toString().c_str(), toString().c_str());
379 return nullptr;
380 }
381 const uint16_t handle = jau::le_to_cpu(ev_cc->handle);
382 const HCIConnectionRef conn = findTrackerConnection(handle);
383 if( nullptr == conn ) {
384 WARN_PRINT("dev_id %u:: ENCRYPT_KEY_REFRESH_COMPLETE: Not tracked conn_handle %s of %s",
385 dev_id, jau::to_hexstring(handle).c_str(), ev.toString().c_str());
386 return nullptr;
387 }
388 return std::make_unique<MgmtEvtHCIEncryptionKeyRefreshComplete>(dev_id, conn->getAddressAndType(), status);
389 }
390 // TODO: AUTH_COMPLETE
391 // 7.7.6 AUTH_COMPLETE 0x06
392
393 default:
394 return nullptr;
395 }
396}
397
398std::unique_ptr<MgmtEvent> HCIHandler::translate(HCICommand& ev) noexcept {
399 const HCIOpcode opc = ev.getOpcode();
400 switch( opc ) {
402 const HCILEEnableEncryptionCmd & ev2 = *static_cast<const HCILEEnableEncryptionCmd*>( &ev );
403 const HCIConnectionRef conn = findTrackerConnection(ev2.getHandle());
404 if( nullptr == conn ) {
405 WARN_PRINT("dev_id %u:: LE_ENABLE_ENC: Not tracked conn_handle %s", dev_id, ev2.toString().c_str());
406 return nullptr;
407 }
408 return std::make_unique<MgmtEvtHCILEEnableEncryptionCmd>(dev_id, conn->getAddressAndType(),
409 ev2.getRand(), ev2.getEDIV(), ev2.getLTK());
410 }
412 const HCILELTKReplyAckCmd & ev2 = *static_cast<const HCILELTKReplyAckCmd*>( &ev );
413 const HCIConnectionRef conn = findTrackerConnection(ev2.getHandle());
414 if( nullptr == conn ) {
415 WARN_PRINT("dev_id %u:: LE_LTK_REPLY_ACK: Not tracked conn_handle %s", dev_id, ev2.toString().c_str());
416 return nullptr;
417 }
418 return std::make_unique<MgmtEvtHCILELTKReplyAckCmd>(dev_id, conn->getAddressAndType(), ev2.getLTK());
419 }
421 const HCILELTKReplyRejCmd & ev2 = *static_cast<const HCILELTKReplyRejCmd*>( &ev );
422 const HCIConnectionRef conn = findTrackerConnection(ev2.getHandle());
423 if( nullptr == conn ) {
424 WARN_PRINT("dev_id %u:: LE_LTK_REPLY_REJ: Not tracked conn_handle %s", dev_id, ev2.toString().c_str());
425 return nullptr;
426 }
427 return std::make_unique<MgmtEvtHCILELTKReplyRejCmd>(dev_id, conn->getAddressAndType());
428 }
429 default:
430 return nullptr;
431 }
432}
433
434std::unique_ptr<const SMPPDUMsg> HCIHandler::getSMPPDUMsg(const HCIACLData::l2cap_frame & l2cap, const uint8_t * l2cap_data) const noexcept {
435 if( nullptr != l2cap_data && 0 < l2cap.len && l2cap.isSMP() ) {
436 return SMPPDUMsg::getSpecialized(l2cap_data, l2cap.len);
437 }
438 return nullptr;
439}
440
441void HCIHandler::hciReaderWork(jau::service_runner& sr) noexcept {
442 jau::snsize_t len;
443 if( !isOpen() ) {
444 // not open
445 ERR_PRINT("Not connected %s", toString().c_str());
446 sr.set_shall_stop();
447 return;
448 }
449
450 len = comm.read(rbuffer.get_wptr(), rbuffer.size(), env.HCI_READER_THREAD_POLL_TIMEOUT);
451 if( 0 < len ) {
452 const jau::nsize_t len2 = static_cast<jau::nsize_t>(len);
453 const HCIPacketType pc = static_cast<HCIPacketType>( rbuffer.get_uint8_nc(0) );
454
455 // ACL
456 if( HCIPacketType::ACLDATA == pc ) {
457 std::unique_ptr<HCIACLData> acldata = HCIACLData::getSpecialized(rbuffer.get_ptr(), len2);
458 if( nullptr == acldata ) {
459 // not valid acl-data ...
460 if( jau::environment::get().verbose ) {
461 WARN_PRINT("dev_id %u: IO RECV Drop ACL (non-acl-data) %s - %s",
462 dev_id, jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str());
463 }
464 return;
465 }
466 const uint8_t* l2cap_data = nullptr; // owned by acldata
467 HCIACLData::l2cap_frame l2cap = acldata->getL2CAPFrame(l2cap_data);
468 std::unique_ptr<const SMPPDUMsg> smpPDU = getSMPPDUMsg(l2cap, l2cap_data);
469 if( nullptr != smpPDU ) {
470 HCIConnectionRef conn = findTrackerConnection(l2cap.handle);
471
472 if( nullptr != conn ) {
473 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV ACL (SMP) %s for %s",
474 dev_id, smpPDU->toString().c_str(), conn->toString().c_str());
475 jau::for_each_fidelity(hciSMPMsgCallbackList, [&](HCISMPMsgCallback &cb) {
476 cb(conn->getAddressAndType(), *smpPDU, l2cap);
477 });
478 } else {
479 WARN_PRINT("dev_id %u: IO RECV ACL Drop (SMP): Not tracked conn_handle %s: %s, %s",
480 dev_id, jau::to_hexstring(l2cap.handle).c_str(),
481 l2cap.toString().c_str(), smpPDU->toString().c_str());
482 }
483 } else if( !l2cap.isGATT() ) { // ignore handled GATT packages
484 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV ACL Drop (L2CAP): ???? %s",
485 dev_id, acldata->toString(l2cap, l2cap_data).c_str());
486 }
487 return;
488 }
489
490 // COMMAND
491 if( HCIPacketType::COMMAND == pc ) {
492 std::unique_ptr<HCICommand> event = HCICommand::getSpecialized(rbuffer.get_ptr(), len2);
493 if( nullptr == event ) {
494 // not a valid event ...
495 ERR_PRINT("IO RECV CMD Drop (non-command) %s - %s",
496 jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str());
497 return;
498 }
499 std::unique_ptr<MgmtEvent> mevent = translate(*event);
500 if( nullptr != mevent ) {
501 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV CMD (CB) %s\n -> %s", dev_id, event->toString().c_str(), mevent->toString().c_str());
502 sendMgmtEvent( *mevent );
503 } else {
504 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV CMD Drop (no translation) %s", dev_id, event->toString().c_str());
505 }
506 return;
507 }
508
509 if( HCIPacketType::EVENT != pc ) {
510 WARN_PRINT("dev_id %u: IO RECV EVT Drop (not event, nor command, nor acl-data) %s - %s",
511 dev_id, jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str());
512 return;
513 }
514
515 // EVENT
516 std::unique_ptr<HCIEvent> event = HCIEvent::getSpecialized(rbuffer.get_ptr(), len2);
517 if( nullptr == event ) {
518 // not a valid event ...
519 ERR_PRINT("IO RECV EVT Drop (non-event) %s - %s",
520 jau::bytesHexString(rbuffer.get_ptr(), 0, len2, true /* lsbFirst*/).c_str(), toString().c_str());
521 return;
522 }
523
524 const HCIMetaEventType mec = event->getMetaEventType();
525 if( HCIMetaEventType::INVALID != mec && !filter_test_metaev(mec) ) {
526 // DROP
527 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV EVT Drop (meta filter) %s", dev_id, event->toString().c_str());
528 return; // next packet
529 }
530
531 if( event->isEvent(HCIEventType::CMD_STATUS) || event->isEvent(HCIEventType::CMD_COMPLETE) )
532 {
533 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV EVT (CMD REPLY) %s", dev_id, event->toString().c_str());
534 if( hciEventRing.isFull() ) {
535 const jau::nsize_t dropCount = hciEventRing.capacity()/4;
536 hciEventRing.drop(dropCount);
537 WARN_PRINT("dev_id %u: IO RECV Drop (%u oldest elements of %u capacity, ring full) - %s",
538 dev_id, dropCount, hciEventRing.capacity(), toString().c_str());
539 }
540 if( !hciEventRing.putBlocking( std::move( event ), jau::fractions_i64::zero ) ) {
541 ERR_PRINT2("hciEventRing put: %s", hciEventRing.toString().c_str());
542 sr.set_shall_stop();
543 return;
544 }
545 } else if( event->isMetaEvent(HCIMetaEventType::LE_ADVERTISING_REPORT) ) {
546 // issue callbacks for the translated AD events
547 jau::darray<std::unique_ptr<EInfoReport>> eirlist = EInfoReport::read_ad_reports(event->getParam(), event->getParamSize());
548 for(jau::nsize_t eircount = 0; eircount < eirlist.size(); ++eircount) {
549 const MgmtEvtDeviceFound e(dev_id, std::move( eirlist[eircount] ) );
550 COND_PRINT(env.DEBUG_SCAN_AD_EIR, "HCIHandler<%u>-IO RECV EVT (AD EIR) [%d] %s",
551 dev_id, eircount, e.getEIR()->toString().c_str());
552 sendMgmtEvent( e );
553 }
554 } else if( event->isMetaEvent(HCIMetaEventType::LE_EXT_ADV_REPORT) ) {
555 // issue callbacks for the translated EAD events
556 jau::darray<std::unique_ptr<EInfoReport>> eirlist = EInfoReport::read_ext_ad_reports(event->getParam(), event->getParamSize());
557 for(jau::nsize_t eircount = 0; eircount < eirlist.size(); ++eircount) {
558 const MgmtEvtDeviceFound e(dev_id, std::move( eirlist[eircount] ) );
559 COND_PRINT(env.DEBUG_SCAN_AD_EIR, "HCIHandler<%u>-IO RECV EVT (EAD EIR (ext)) [%d] %s",
560 dev_id, eircount, e.getEIR()->toString().c_str());
561 sendMgmtEvent( e );
562 }
563 } else {
564 // issue a callback for the translated event
565 std::unique_ptr<MgmtEvent> mevent = translate(*event);
566 if( nullptr != mevent ) {
567 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV EVT (CB) %s\n -> %s", dev_id, event->toString().c_str(), mevent->toString().c_str());
568 sendMgmtEvent( *mevent );
569 } else {
570 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV EVT Drop (no translation) %s", dev_id, event->toString().c_str());
571 }
572 }
573 } else if( 0 > len && ETIMEDOUT != errno && !comm.interrupted() ) { // expected exits
574 ERR_PRINT("HCIComm read: Error res %d, %s", len, toString().c_str());
575 // Keep alive - sr.set_shall_stop();
576 } else if( ETIMEDOUT != errno && !comm.interrupted() ) { // expected TIMEOUT if idle
577 WORDY_PRINT("HCIHandler<%u>::reader: HCIComm read: IRQed res %d, %s", dev_id, len, toString().c_str());
578 }
579}
580
581void HCIHandler::hciReaderEndLocked(jau::service_runner& sr) noexcept {
582 (void)sr;
583 WORDY_PRINT("HCIHandler<%u>::reader: Ended. Ring has %u entries flushed - %s", dev_id, hciEventRing.size(), toString().c_str());
584 hciEventRing.clear();
585}
586
587
588void HCIHandler::sendMgmtEvent(const MgmtEvent& event) noexcept {
589 MgmtEventCallbackList & mgmtEventCallbackList = mgmtEventCallbackLists[static_cast<uint16_t>(event.getOpcode())];
590 int invokeCount = 0;
591
592 jau::for_each_fidelity(mgmtEventCallbackList, [&](MgmtEventCallback &cb) {
593 try {
594 cb(event);
595 } catch (std::exception &e) {
596 ERR_PRINT("CBs %d/%zd: MgmtEventCallback %s : Caught exception %s - %s",
597 invokeCount+1, mgmtEventCallbackList.size(),
598 cb.toString().c_str(), e.what(), toString().c_str());
599 }
600 invokeCount++;
601 });
602
603 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>::sendMgmtEvent: Event %s -> %d/%zd callbacks",
604 dev_id, event.toString().c_str(), invokeCount, mgmtEventCallbackList.size());
605 (void)invokeCount;
606}
607
608bool HCIHandler::sendCommand(HCICommand &req, const bool quiet) noexcept {
609 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO SENT %s", dev_id, req.toString().c_str());
610
611 jau::TROOctets & pdu = req.getPDU();
612 if ( comm.write( pdu.get_ptr(), pdu.size() ) < 0 ) {
613 if( !quiet || jau::environment::get().verbose ) {
614 ERR_PRINT("HCIComm write error, req %s - %s", req.toString().c_str(), toString().c_str());
615 }
616 return false;
617 }
618 return true;
619}
620
621std::unique_ptr<HCIEvent> HCIHandler::getNextReply(HCICommand &req, int32_t & retryCount, const jau::fraction_i64& replyTimeout) noexcept
622{
623 // Ringbuffer read is thread safe
624 while( retryCount < env.HCI_READ_PACKET_MAX_RETRY ) {
625 std::unique_ptr<HCIEvent> ev;
626 if( !hciEventRing.getBlocking(ev, replyTimeout) || nullptr == ev ) {
627 errno = ETIMEDOUT;
628 ERR_PRINT("nullptr result (timeout %" PRIi64 " ms -> abort): req %s - %s", replyTimeout.to_ms(), req.toString().c_str(), toString().c_str());
629 return nullptr;
630 } else if( !ev->validate(req) ) {
631 // This could occur due to an earlier timeout w/ a nullptr == res (see above),
632 // i.e. the pending reply processed here and naturally not-matching.
633 retryCount++;
634 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV getNextReply: res mismatch (drop, retry %d): res %s; req %s",
635 dev_id, retryCount, ev->toString().c_str(), req.toString().c_str());
636 } else {
637 COND_PRINT(env.DEBUG_EVENT, "HCIHandler<%u>-IO RECV getNextReply: res %s; req %s", dev_id, ev->toString().c_str(), req.toString().c_str());
638 return ev;
639 }
640 }
641 return nullptr;
642}
643
644std::unique_ptr<HCIEvent> HCIHandler::getNextCmdCompleteReply(HCICommand &req, HCICommandCompleteEvent **res) noexcept {
645 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
646
647 *res = nullptr;
648
649 int32_t retryCount = 0;
650 std::unique_ptr<HCIEvent> ev = nullptr;
651
652 while( retryCount < env.HCI_READ_PACKET_MAX_RETRY ) {
653 ev = getNextReply(req, retryCount, env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT);
654 if( nullptr == ev ) {
655 break; // timeout, leave loop
656 } else if( ev->isEvent(HCIEventType::CMD_COMPLETE) ) {
657 // gotcha, leave loop
658 *res = static_cast<HCICommandCompleteEvent*>(ev.get());
659 break;
660 } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
661 // pending command .. wait for result
662 HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
663 HCIStatusCode status = ev_cs->getStatus();
664 if( HCIStatusCode::SUCCESS != status ) {
665 DBG_WARN_PRINT("dev_id %u: CMD_STATUS 0x%2.2X (%s), errno %d %s: res %s, req %s - %s",
666 dev_id, number(status), to_string(status).c_str(), errno, strerror(errno),
667 ev_cs->toString().c_str(), req.toString().c_str(), toString().c_str());
668 break; // error status, leave loop
669 } else {
670 DBG_PRINT("HCIHandler<%u>::getNextCmdCompleteReply: CMD_STATUS 0x%2.2X (%s, retryCount %d), errno %d %s: res %s, req %s - %s",
671 dev_id, number(status), to_string(status).c_str(), retryCount, errno, strerror(errno),
672 ev_cs->toString().c_str(), req.toString().c_str(), toString().c_str());
673 }
674 retryCount++;
675 continue; // next packet
676 } else {
677 retryCount++;
678 DBG_PRINT("HCIHandler<%u>::getNextCmdCompleteReply: !(CMD_COMPLETE, CMD_STATUS) (drop, retry %d): res %s; req %s - %s",
679 dev_id, retryCount, ev->toString().c_str(), req.toString().c_str(), toString().c_str());
680 continue; // next packet
681 }
682 }
683 return ev;
684}
685
686HCIHandler::HCIHandler(const uint16_t dev_id_, const BTMode btMode_) noexcept
687: env(HCIEnv::get()),
688 dev_id(dev_id_),
689 rbuffer(HCI_MAX_MTU, jau::lb_endian_t::little),
690 comm(dev_id_, HCI_CHANNEL_RAW),
691 hci_reader_service("HCIHandler::reader", THREAD_SHUTDOWN_TIMEOUT_MS,
692 jau::bind_member(this, &HCIHandler::hciReaderWork),
694 jau::bind_member(this, &HCIHandler::hciReaderEndLocked)),
695 hciEventRing(env.HCI_EVT_RING_CAPACITY),
696 le_ll_feats( LE_Features::NONE ),
697 sup_commands_set( false ),
698 allowClose( comm.is_open() ),
699 btMode(btMode_),
700 currentScanType(ScanType::NONE),
701 advertisingEnabled(false)
702{
703 zeroSupCommands();
704
705 WORDY_PRINT("HCIHandler<%u>.ctor: Start %s", dev_id, toString().c_str());
706 if( !allowClose ) {
707 ERR_PRINT("Could not open hci control channel %s", toString().c_str());
708 return;
709 }
710
711 comm.set_interrupted_query( jau::bind_member(&hci_reader_service, &jau::service_runner::shall_stop2) );
712 hci_reader_service.start();
713
714 PERF_TS_T0();
715
716#if 0
717 {
718 int opt = 1;
719 if (setsockopt(comm.socket(), SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
720 ERR_PRINT("setsockopt SO_TIMESTAMP %s", toString().c_str());
721 goto fail;
722 }
723
724 if (setsockopt(comm.socket(), SOL_SOCKET, SO_PASSCRED, &opt, sizeof(opt)) < 0) {
725 ERR_PRINT("setsockopt SO_PASSCRED %s", toString().c_str());
726 goto fail;
727 }
728 }
729#endif
730
731#define FILTER_ALL_EVENTS 0
732
733 // Mandatory socket filter (not adapter filter!)
734 {
735#if 0
736 // No use for pre-existing hci_ufilter
737 hci_ufilter of;
738 socklen_t olen;
739
740 olen = sizeof(of);
741 if (getsockopt(comm.socket(), SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
742 ERR_PRINT("getsockopt %s", toString().c_str());
743 goto fail;
744 }
745#endif
746 HCIComm::filter_clear(&filter_mask);
747 if constexpr ( CONSIDER_HCI_CMD_FOR_SMP_STATE ) {
748 // Currently only used to determine ENCRYPTION STATE, if at all.
749 HCIComm::filter_set_ptype(number(HCIPacketType::COMMAND), &filter_mask); // COMMANDs
750 }
751 HCIComm::filter_set_ptype(number(HCIPacketType::EVENT), &filter_mask); // EVENTs
752 HCIComm::filter_set_ptype(number(HCIPacketType::ACLDATA), &filter_mask); // SMP via ACL DATA
753
754 // Setup generic filter mask for all events, this is also required for
755#if FILTER_ALL_EVENTS
756 HCIComm::filter_all_events(&filter_mask); // all events
757#else
766
767 // HCIComm::filter_set_event(number(HCIEventType::IO_CAPABILITY_REQUEST), &filter_mask);
768 // HCIComm::filter_set_event(number(HCIEventType::IO_CAPABILITY_RESPONSE), &filter_mask);
770 // HCIComm::filter_set_event(number(HCIEventType::DISCONN_PHY_LINK_COMPLETE), &filter_mask);
771 // HCIComm::filter_set_event(number(HCIEventType::DISCONN_LOGICAL_LINK_COMPLETE), &filter_mask);
772#endif
773 HCIComm::filter_set_opcode(0, &filter_mask); // all opcode
774
775 if (setsockopt(comm.socket(), SOL_HCI, HCI_FILTER, &filter_mask, sizeof(filter_mask)) < 0) {
776 ERR_PRINT("setsockopt HCI_FILTER %s", toString().c_str());
777 goto fail;
778 }
779 }
780 // Mandatory own LE_META filter
781 {
782 uint32_t mask = 0;
783#if FILTER_ALL_EVENTS
784 filter_all_metaevs(mask);
785#else
786 filter_set_metaev(HCIMetaEventType::LE_CONN_COMPLETE, mask);
787 filter_set_metaev(HCIMetaEventType::LE_ADVERTISING_REPORT, mask);
788 filter_set_metaev(HCIMetaEventType::LE_REMOTE_FEAT_COMPLETE, mask);
789 filter_set_metaev(HCIMetaEventType::LE_LTK_REQUEST, mask);
790 filter_set_metaev(HCIMetaEventType::LE_EXT_CONN_COMPLETE, mask);
791 filter_set_metaev(HCIMetaEventType::LE_PHY_UPDATE_COMPLETE, mask);
792 filter_set_metaev(HCIMetaEventType::LE_EXT_ADV_REPORT, mask);
793 // filter_set_metaev(HCIMetaEventType::LE_CHANNEL_SEL_ALGO, mask);
794
795#endif
796 filter_put_metaevs(mask);
797 }
798 // Own HCIOpcodeBit/HCIOpcode filter (not functional yet!)
799 {
800 uint64_t mask = 0;
801#if FILTER_ALL_EVENTS
802 filter_all_opcbit(mask);
803#else
804 filter_set_opcbit(HCIOpcodeBit::CREATE_CONN, mask);
805 filter_set_opcbit(HCIOpcodeBit::DISCONNECT, mask);
806 // filter_set_opcbit(HCIOpcodeBit::IO_CAPABILITY_REQ_REPLY, mask);
807 // filter_set_opcbit(HCIOpcodeBit::IO_CAPABILITY_REQ_NEG_REPLY, mask);
808 filter_set_opcbit(HCIOpcodeBit::RESET, mask);
809 filter_set_opcbit(HCIOpcodeBit::READ_LOCAL_VERSION, mask);
810 filter_set_opcbit(HCIOpcodeBit::READ_LOCAL_COMMANDS, mask);
811 filter_set_opcbit(HCIOpcodeBit::LE_SET_ADV_PARAM, mask);
812 filter_set_opcbit(HCIOpcodeBit::LE_SET_ADV_DATA, mask);
813 filter_set_opcbit(HCIOpcodeBit::LE_SET_SCAN_RSP_DATA, mask);
814 filter_set_opcbit(HCIOpcodeBit::LE_SET_ADV_ENABLE, mask);
815 filter_set_opcbit(HCIOpcodeBit::LE_SET_SCAN_PARAM, mask);
816 filter_set_opcbit(HCIOpcodeBit::LE_SET_SCAN_ENABLE, mask);
817 filter_set_opcbit(HCIOpcodeBit::LE_CREATE_CONN, mask);
818 filter_set_opcbit(HCIOpcodeBit::LE_READ_REMOTE_FEATURES, mask);
819 filter_set_opcbit(HCIOpcodeBit::LE_ENABLE_ENC, mask);
820 filter_set_opcbit(HCIOpcodeBit::LE_LTK_REPLY_ACK, mask);
821 filter_set_opcbit(HCIOpcodeBit::LE_LTK_REPLY_REJ, mask);
822 filter_set_opcbit(HCIOpcodeBit::LE_READ_PHY, mask);
823 filter_set_opcbit(HCIOpcodeBit::LE_SET_DEFAULT_PHY, mask);
824 filter_set_opcbit(HCIOpcodeBit::LE_SET_PHY, mask);
825 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_ADV_PARAMS, mask);
826 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_ADV_DATA, mask);
827 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_RSP_DATA, mask);
828 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_ADV_ENABLE, mask);
829 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_PARAMS, mask);
830 filter_set_opcbit(HCIOpcodeBit::LE_SET_EXT_SCAN_ENABLE, mask);
831 filter_set_opcbit(HCIOpcodeBit::LE_EXT_CREATE_CONN, mask);
832#endif
833 filter_put_opcbit(mask);
834 }
835 zeroSupCommands();
836
837 PERF_TS_TD("HCIHandler::ctor.ok");
838 WORDY_PRINT("HCIHandler.ctor: End OK - %s", toString().c_str());
839 return;
840
841fail:
842 close();
843 PERF_TS_TD("HCIHandler::ctor.fail");
844 WORDY_PRINT("HCIHandler.ctor: End failure - %s", toString().c_str());
845 return;
846}
847
848void HCIHandler::zeroSupCommands() noexcept {
849 bzero(sup_commands, sizeof(sup_commands));
850 sup_commands_set = false;
851 le_ll_feats = LE_Features::NONE;
852}
853bool HCIHandler::initSupCommands() noexcept {
854 // We avoid using a lock or an atomic-switch as we rely on sensible calls.
855 if( !isOpen() ) {
856 zeroSupCommands();
857 return false;
858 }
859 HCIStatusCode status;
860
861 le_ll_feats = LE_Features::NONE;
862 {
864 const hci_rp_le_read_local_features * ev_lf;
865 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_lf, &status, true /* quiet */);
866 if( nullptr == ev || nullptr == ev_lf || HCIStatusCode::SUCCESS != status ) {
867 DBG_PRINT("HCIHandler<%u>::initSupCommands: LE_READ_LOCAL_FEATURES: 0x%x (%s) - %s",
868 number(status), to_string(status).c_str(), toString().c_str());
869 zeroSupCommands();
870 return false;
871 }
872 le_ll_feats = static_cast<LE_Features>( jau::get_uint64(ev_lf->features + 0, jau::lb_endian_t::little) );
873 }
874
876 const hci_rp_read_local_commands * ev_cmds;
877 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_cmds, &status, true /* quiet */);
878 if( nullptr == ev || nullptr == ev_cmds || HCIStatusCode::SUCCESS != status ) {
879 DBG_PRINT("HCIHandler<%u>::initSupCommands: READ_LOCAL_COMMANDS: 0x%x (%s) - %s",
880 dev_id, number(status), to_string(status).c_str(), toString().c_str());
881 zeroSupCommands();
882 return false;
883 } else {
884 memcpy(sup_commands, ev_cmds->commands, sizeof(sup_commands));
885 sup_commands_set = true;
886 return true;
887 }
888}
889
890HCIStatusCode HCIHandler::check_open_connection(const std::string& caller,
891 const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType,
892 const bool addUntrackedConn) {
893 if( !isOpen() ) {
894 ERR_PRINT("%s: Not connected %s", caller.c_str(), toString().c_str());
896 }
897 if( 0 == conn_handle ) {
898 ERR_PRINT("%s: Null conn_handle given address%s (drop) - %s",
899 caller.c_str(), peerAddressAndType.toString().c_str(), toString().c_str());
901 }
902 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
903 HCIConnectionRef conn = findTrackerConnection(conn_handle);
904 if( nullptr == conn ) {
905 // called w/o being connected through this HCIHandler
906 if( addUntrackedConn ) {
907 // add unknown connection to tracker
908 conn = addOrUpdateTrackerConnection(peerAddressAndType, conn_handle);
909 WORDY_PRINT("HCIHandler::%s: Not tracked address%s, added %s - %s",
910 caller.c_str(), peerAddressAndType.toString().c_str(),
911 conn->toString().c_str(), toString().c_str());
912 } else {
913 ERR_PRINT("%s: Not tracked handle %s (address%s) (drop) - %s",
914 caller.c_str(), jau::to_hexstring(conn_handle).c_str(),
915 peerAddressAndType.toString().c_str(), toString().c_str());
917 }
918 } else if( !conn->equals(peerAddressAndType) ) {
919 ERR_PRINT("%s: Mismatch given address%s and tracked %s (drop) - %s",
920 caller.c_str(), peerAddressAndType.toString().c_str(),
921 conn->toString().c_str(), toString().c_str());
923 }
924 DBG_PRINT("HCIHandler<%u>::%s: address%s, handle %s, %s - %s",
925 dev_id, caller.c_str(), peerAddressAndType.toString().c_str(),
926 jau::to_hexstring(conn_handle).c_str(),
927 conn->toString().c_str(), toString().c_str());
928
930}
931
932HCIStatusCode HCIHandler::le_read_remote_features(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType) noexcept {
933 HCIStatusCode status = check_open_connection("le_read_remote_features", conn_handle, peerAddressAndType);
934 if( HCIStatusCode::SUCCESS != status ) {
935 return status;
936 }
938 hci_cp_le_read_remote_features * cp = req0.getWStruct();
939 cp->handle = jau::cpu_to_le(conn_handle);
940 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
941
942 if( nullptr == ev || HCIStatusCode::SUCCESS != status ) {
943 ERR_PRINT("le_read_remote_features: LE_READ_PHY: 0x%x (%s) - %s",
944 number(status), to_string(status).c_str(), toString().c_str());
945 }
946 return status;
947}
948
949void HCIHandler::close() noexcept {
950 // Avoid disconnect re-entry -> potential deadlock
951 bool expConn = true; // C++11, exp as value since C++20
952 if( !allowClose.compare_exchange_strong(expConn, false) ) {
953 // not open
954 const bool hci_service_stopped = hci_reader_service.join(); // [data] race: wait until disconnecting thread has stopped service
955 comm.close();
956 DBG_PRINT("HCIHandler<%u>::close: Not open: stopped %d, %s", dev_id, hci_service_stopped, toString().c_str());
958 resetAllStates(false);
959 comm.close();
960 return;
961 }
962 PERF_TS_T0();
963 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
964 DBG_PRINT("HCIHandler<%u>::close: Start %s", dev_id, toString().c_str());
966 resetAllStates(false);
967
968 PERF_TS_TD("HCIHandler::close.1");
969 hci_reader_service.stop();
970 comm.close();
971 PERF_TS_TD("HCIHandler::close.X");
972
973 DBG_PRINT("HCIHandler<%u>::close: End %s", dev_id, toString().c_str());
974}
975
976std::string HCIHandler::toString() const noexcept {
977 return "HCIHandler["+std::to_string(dev_id)+", BTMode "+to_string(btMode)+", open "+std::to_string(isOpen())+
978 ", adv "+std::to_string(advertisingEnabled)+", scan "+to_string(currentScanType)+
979 ", ext[init "+std::to_string(sup_commands_set)+", adv "+std::to_string(use_ext_adv())+", scan "+std::to_string(use_ext_scan())+", conn "+std::to_string(use_ext_conn())+
980 "], ring[entries "+std::to_string(hciEventRing.size())+"]]";
981}
982
983HCIStatusCode HCIHandler::startAdapter() {
984 if( !isOpen() ) {
985 ERR_PRINT("Not connected %s", toString().c_str());
987 }
989
990 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
991 DBG_PRINT("HCIHandler<%u>::startAdapter.0: %s", dev_id, toString().c_str());
992
993 #if defined(__linux__)
994 int res_ioctl;
995 if( ( res_ioctl = ioctl(comm.socket(), HCIDEVUP, dev_id) ) < 0 ) {
996 if (errno != EALREADY) {
997 ERR_PRINT("FAILED: %d - %s", res_ioctl, toString().c_str());
998 } else {
1000 }
1001 } else {
1003 }
1004 #elif defined(__FreeBSD__)
1005 // #warning add implementation
1006 ABORT("add implementation for FreeBSD");
1007 #else
1008 #warning add implementation
1009 ABORT("add implementation");
1010 #endif
1011 if( HCIStatusCode::SUCCESS == res ) {
1013 }
1014 DBG_PRINT("HCIHandler<%u>::startAdapter.X: %s - %s", dev_id, to_string(res).c_str(), toString().c_str());
1015 return res;
1016}
1017
1018HCIStatusCode HCIHandler::stopAdapter() {
1019 if( !isOpen() ) {
1020 ERR_PRINT("Not connected %s", toString().c_str());
1022 }
1024
1025 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1026 DBG_PRINT("HCIHandler<%u>::stopAdapter.0: %s", dev_id, toString().c_str());
1027
1028 #if defined(__linux__)
1029 int res_ioctl;
1030 if( ( res_ioctl = ioctl(comm.socket(), HCIDEVDOWN, dev_id) ) < 0) {
1031 ERR_PRINT("FAILED: %d - %s", res_ioctl, toString().c_str());
1032 } else {
1034 }
1035 #elif defined(__FreeBSD__)
1036 // #warning add implementation
1037 ABORT("add implementation for FreeBSD");
1038 #else
1039 #warning add implementation
1040 ABORT("add implementation");
1041 #endif
1042 if( HCIStatusCode::SUCCESS == res ) {
1043 resetAllStates(false);
1044 }
1045 DBG_PRINT("HCIHandler<%u>::stopAdapter.X: %s - %s", dev_id, to_string(res).c_str(), toString().c_str());
1046 return res;
1047}
1048
1050 if( !isOpen() ) {
1051 ERR_PRINT("Not connected %s", toString().c_str());
1053 }
1055 bool user_called = false;
1056 bool user_abort = false;
1057
1058 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1059 DBG_PRINT("HCIHandler<%u>::resetAdapter.0: %s", dev_id, toString().c_str());
1060
1061 #if defined(__linux__)
1062 res = stopAdapter();
1063 if( HCIStatusCode::SUCCESS == res ) {
1064 if( nullptr != user_post_shutdown ) {
1065 user_called = true;
1066 res = user_post_shutdown();
1067 user_abort = HCIStatusCode::SUCCESS != res;
1068 }
1069 if( !user_abort ) {
1070 res = startAdapter();
1071 }
1072 }
1073 #elif defined(__FreeBSD__)
1074 // #warning add implementation
1075 (void)user_post_shutdown;
1076 ABORT("add implementation for FreeBSD");
1077 #else
1078 #warning add implementation
1079 (void)user_post_shutdown;
1080 ABORT("add implementation");
1081 #endif
1082 DBG_PRINT("HCIHandler<%u>::resetAdapter.X: %s user[called %d, abort %d] - %s", dev_id, to_string(res).c_str(), user_called, user_abort, toString().c_str());
1083 return res;
1084}
1085
1086bool HCIHandler::resetAllStates(const bool powered_on) noexcept {
1087 const std::lock_guard<std::recursive_mutex> lock(mtx_connectionList); // RAII-style acquire and relinquish via destructor
1088 connectionList.clear();
1089 disconnectCmdList.clear();
1090 currentScanType = ScanType::NONE;
1091 advertisingEnabled = false;
1092 zeroSupCommands();
1093 if( powered_on ) {
1094 return initSupCommands();
1095 } else {
1096 return true;
1097 }
1098}
1099
1100HCIStatusCode HCIHandler::resetHCI() noexcept {
1102
1103 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1104 DBG_PRINT("HCIHandler<%u>::Reset HCI.0: %s", dev_id, toString().c_str());
1105
1107
1108 const hci_rp_status * ev_status;
1109 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &res);
1110 if( nullptr == ev ) {
1111 res = HCIStatusCode::INTERNAL_TIMEOUT; // timeout
1112 }
1113 DBG_PRINT("HCIHandler<%u>::Reset HCI.X: %s - %s", dev_id, to_string(res).c_str(), toString().c_str());
1114 return res;
1115}
1116
1118 if( !isOpen() ) {
1119 ERR_PRINT("Not connected %s", toString().c_str());
1121 }
1123 const hci_rp_read_local_version * ev_lv;
1124 HCIStatusCode status;
1125 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_lv, &status);
1126 if( nullptr == ev || nullptr == ev_lv || HCIStatusCode::SUCCESS != status ) {
1127 ERR_PRINT("%s: 0x%x (%s) - %s",
1128 to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1129 bzero(&version, sizeof(version));
1130 } else {
1131 version.hci_ver = ev_lv->hci_ver;
1132 version.hci_rev = jau::le_to_cpu(ev_lv->hci_rev);
1133 version.manufacturer = jau::le_to_cpu(ev_lv->manufacturer);
1134 version.lmp_ver = ev_lv->lmp_ver;
1135 version.lmp_subver = jau::le_to_cpu(ev_lv->lmp_subver);
1136 }
1137 return status;
1138}
1139
1140HCIStatusCode HCIHandler::le_set_scan_param(const bool le_scan_active,
1141 const HCILEOwnAddressType own_mac_type,
1142 const uint16_t le_scan_interval, const uint16_t le_scan_window,
1143 const uint8_t filter_policy) noexcept {
1144 if( !isOpen() ) {
1145 ERR_PRINT("Not connected %s", toString().c_str());
1147 }
1148 if( is_set(currentScanType, ScanType::LE) ) {
1149 WARN_PRINT("Not allowed: LE Scan Enabled: %s - tried scan [interval %.3f ms, window %.3f ms]",
1150 toString().c_str(), 0.625f * (float)le_scan_interval, 0.625f * (float)le_scan_window);
1152 }
1153 DBG_PRINT("HCIHandler<%u>::le_set_scan_param: scan [active %d, interval %.3f ms, window %.3f ms, filter %d] - %s",
1154 dev_id, le_scan_active,
1155 0.625f * (float)le_scan_interval, 0.625f * (float)le_scan_window,
1156 filter_policy, toString().c_str());
1157
1158 HCIStatusCode status;
1159 if( use_ext_scan() ) {
1160 struct le_set_ext_scan_params {
1161 __u8 own_address_type;
1163 __u8 scanning_phys;
1164 hci_cp_le_scan_phy_params p1;
1165 // hci_cp_le_scan_phy_params p2;
1166 } __packed;
1168 le_set_ext_scan_params * cp = req0.getWStruct();
1169 cp->own_address_type = static_cast<uint8_t>(own_mac_type);
1170 cp->filter_policy = filter_policy;
1171 cp->scanning_phys = direct_bt::number(LE_PHYs::LE_1M); // Only scan on LE_1M for compatibility
1172
1173 cp->p1.type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE;
1174 cp->p1.interval = jau::cpu_to_le(le_scan_interval);
1175 cp->p1.window = jau::cpu_to_le(le_scan_window);
1176 // TODO: Support LE_1M + LE_CODED combo?
1177
1178 const hci_rp_status * ev_status;
1179 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1180 } else {
1182 hci_cp_le_set_scan_param * cp = req0.getWStruct();
1183 cp->type = le_scan_active ? LE_SCAN_ACTIVE : LE_SCAN_PASSIVE;
1184 cp->interval = jau::cpu_to_le(le_scan_interval);
1185 cp->window = jau::cpu_to_le(le_scan_window);
1186 cp->own_address_type = static_cast<uint8_t>(own_mac_type);
1187 cp->filter_policy = filter_policy;
1188
1189 const hci_rp_status * ev_status;
1190 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1191 }
1192 return status;
1193}
1194
1195HCIStatusCode HCIHandler::le_enable_scan(const bool enable, const bool filter_dup) noexcept {
1196 if( !isOpen() ) {
1197 ERR_PRINT("Not connected %s", toString().c_str());
1199 }
1200 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1201
1202 if( enable && advertisingEnabled ) {
1203 WARN_PRINT("dev_id %u: Not allowed: Advertising is enabled %s", dev_id, toString().c_str());
1205 }
1206 ScanType nextScanType = changeScanType(currentScanType, ScanType::LE, enable);
1207 DBG_PRINT("HCIHandler<%u>::le_enable_scan: enable %s -> %s, filter_dup %d - %s",
1208 dev_id, to_string(currentScanType).c_str(), to_string(nextScanType).c_str(), filter_dup, toString().c_str());
1209
1210 HCIStatusCode status;
1211 if( currentScanType != nextScanType ) {
1212 if( use_ext_scan() ) {
1214 hci_cp_le_set_ext_scan_enable * cp = req0.getWStruct();
1215 cp->enable = enable ? LE_SCAN_ENABLE : LE_SCAN_DISABLE;
1216 cp->filter_dup = filter_dup ? LE_SCAN_FILTER_DUP_ENABLE : LE_SCAN_FILTER_DUP_DISABLE;
1217 // cp->duration = 0; // until disabled
1218 // cp->period = 0; // until disabled
1219 const hci_rp_status * ev_status;
1220 std::unique_ptr<HCIEvent> evComplete = processCommandComplete(req0, &ev_status, &status);
1221 } else {
1223 hci_cp_le_set_scan_enable * cp = req0.getWStruct();
1224 cp->enable = enable ? LE_SCAN_ENABLE : LE_SCAN_DISABLE;
1225 cp->filter_dup = filter_dup ? LE_SCAN_FILTER_DUP_ENABLE : LE_SCAN_FILTER_DUP_DISABLE;
1226 const hci_rp_status * ev_status;
1227 std::unique_ptr<HCIEvent> evComplete = processCommandComplete(req0, &ev_status, &status);
1228 }
1229 } else {
1230 status = HCIStatusCode::SUCCESS;
1231 WARN_PRINT("dev_id %u: current %s == next %s, OK, skip command - %s",
1232 dev_id, to_string(currentScanType).c_str(), to_string(nextScanType).c_str(), toString().c_str());
1233 }
1234
1235 if( HCIStatusCode::SUCCESS == status ) {
1236 currentScanType = nextScanType;
1237 const MgmtEvtDiscovering e(dev_id, ScanType::LE, enable);
1238 sendMgmtEvent( e );
1239 }
1240 return status;
1241}
1242
1244 const bool le_scan_active,
1245 const HCILEOwnAddressType own_mac_type,
1246 const uint16_t le_scan_interval, const uint16_t le_scan_window,
1247 const uint8_t filter_policy) noexcept {
1248 if( !isOpen() ) {
1249 ERR_PRINT("Not connected %s", toString().c_str());
1251 }
1252 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1253
1254 if( advertisingEnabled ) {
1255 WARN_PRINT("dev_id %u: Not allowed: Advertising is enabled %s", dev_id, toString().c_str());
1257 }
1258 if( is_set(currentScanType, ScanType::LE) ) {
1259 WARN_PRINT("dev_id %u: Not allowed: LE Scan Enabled: %s", dev_id, toString().c_str());
1261 }
1262 HCIStatusCode status = le_set_scan_param(le_scan_active, own_mac_type, le_scan_interval, le_scan_window, filter_policy);
1263 if( HCIStatusCode::SUCCESS != status ) {
1264 WARN_PRINT("dev_id %u: le_set_scan_param failed: %s - %s",
1265 dev_id, to_string(status).c_str(), toString().c_str());
1266 return status;
1267 }
1268 status = le_enable_scan(true /* enable */, filter_dup);
1269 if( HCIStatusCode::SUCCESS != status ) {
1270 WARN_PRINT("dev_id %u: le_enable_scan failed: %s - %s",
1271 dev_id, to_string(status).c_str(), toString().c_str());
1272 }
1273 return status;
1274}
1275
1277 const HCILEPeerAddressType peer_mac_type,
1278 const HCILEOwnAddressType own_mac_type,
1279 const uint16_t le_scan_interval, const uint16_t le_scan_window,
1280 const uint16_t conn_interval_min, const uint16_t conn_interval_max,
1281 const uint16_t conn_latency, const uint16_t supervision_timeout) noexcept {
1282 /**
1283 * As we rely on consistent 'pending tracker connections',
1284 * i.e. avoid a race condition on issuing connections via this command,
1285 * we need to synchronize this method.
1286 */
1287 const std::lock_guard<std::mutex> lock(mtx_connect_cmd); // RAII-style acquire and relinquish via destructor
1288
1289 if( !isOpen() ) {
1290 ERR_PRINT("Not connected %s", toString().c_str());
1292 }
1293 if( advertisingEnabled ) {
1294 WARN_PRINT("Not allowed: Advertising is enabled %s", toString().c_str());
1296 }
1297
1298 const uint16_t min_ce_length = 0x0000;
1299 const uint16_t max_ce_length = 0x0000;
1300 const uint8_t initiator_filter = 0x00; // whitelist not used but peer_bdaddr*
1301
1302 DBG_PRINT("HCIHandler<%u>::le_create_conn: scan [interval %.3f ms, window %.3f ms]", 0.625f *
1303 dev_id, (float)le_scan_interval, 0.625f * (float)le_scan_window);
1304 DBG_PRINT("HCIHandler<%u>::le_create_conn: conn [interval [%.3f ms - %.3f ms], latency %d, sup_timeout %d ms] - %s",
1305 dev_id, 1.25f * (float)conn_interval_min, 1.25f * (float)conn_interval_max,
1306 conn_latency, supervision_timeout*10, toString().c_str());
1307
1308 size_type pendingConnections = countPendingTrackerConnections();
1309 if( 0 < pendingConnections ) {
1310 DBG_PRINT("HCIHandler<%u>::le_create_conn: %zu connections pending - %s", dev_id, (size_t)pendingConnections, toString().c_str());
1311 jau::fraction_i64 td = 0_s;
1312 while( env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT > td && 0 < pendingConnections ) {
1313 sleep_for(env.HCI_COMMAND_POLL_PERIOD);
1314 td += env.HCI_COMMAND_POLL_PERIOD;
1315 pendingConnections = countPendingTrackerConnections();
1316 }
1317 if( 0 < pendingConnections ) {
1318 WARN_PRINT("%zu connections pending after %" PRIi64 " ms - %s", (size_t)pendingConnections, td.to_ms(), toString().c_str());
1319 } else {
1320 DBG_PRINT("HCIHandler<%u>::le_create_conn: pending connections resolved after %" PRIi64 " ms - %s", dev_id, td.to_ms(), toString().c_str());
1321 }
1322 }
1323 const BDAddressAndType addressAndType(peer_bdaddr, to_BDAddressType(peer_mac_type));
1324 HCIConnectionRef disconn = findDisconnectCmd(addressAndType);
1325 if( nullptr != disconn ) {
1326 DBG_PRINT("HCIHandler<%u>::le_create_conn: disconnect pending %s - %s",
1327 dev_id, disconn->toString().c_str(), toString().c_str());
1328 jau::fraction_i64 td = 0_s;
1329 while( env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT > td && nullptr != disconn ) {
1330 sleep_for(env.HCI_COMMAND_POLL_PERIOD);
1331 td += env.HCI_COMMAND_POLL_PERIOD;
1332 disconn = findDisconnectCmd(addressAndType);
1333 }
1334 if( nullptr != disconn ) {
1335 WARN_PRINT("disconnect persisting after %" PRIi64 " ms: %s - %s",
1336 td.to_ms(), disconn->toString().c_str(), toString().c_str());
1337 } else {
1338 DBG_PRINT("HCIHandler<%u>::le_create_conn: disconnect resolved after %" PRIi64 " ms - %s", dev_id, td.to_ms(), toString().c_str());
1339 }
1340 }
1341 HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, 0);
1342 HCIStatusCode status;
1343
1344 if( use_ext_conn() ) {
1345 struct le_ext_create_conn {
1347 __u8 own_address_type;
1348 __u8 peer_addr_type;
1349 bdaddr_t peer_addr;
1350 __u8 phys;
1351 hci_cp_le_ext_conn_param p1;
1352 // hci_cp_le_ext_conn_param p2;
1353 // hci_cp_le_ext_conn_param p3;
1354 } __packed;
1356 le_ext_create_conn * cp = req0.getWStruct();
1357 cp->filter_policy = initiator_filter;
1358 cp->own_address_type = static_cast<uint8_t>(own_mac_type);
1359 cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type);
1360 cp->peer_addr = jau::cpu_to_le(peer_bdaddr);
1361 cp->phys = direct_bt::number(LE_PHYs::LE_1M); // Only scan on LE_1M for compatibility
1362
1363 cp->p1.scan_interval = jau::cpu_to_le(le_scan_interval);
1364 cp->p1.scan_window = jau::cpu_to_le(le_scan_window);
1365 cp->p1.conn_interval_min = jau::cpu_to_le(conn_interval_min);
1366 cp->p1.conn_interval_max = jau::cpu_to_le(conn_interval_max);
1367 cp->p1.conn_latency = jau::cpu_to_le(conn_latency);
1368 cp->p1.supervision_timeout = jau::cpu_to_le(supervision_timeout);
1369 cp->p1.min_ce_len = jau::cpu_to_le(min_ce_length);
1370 cp->p1.max_ce_len = jau::cpu_to_le(max_ce_length);
1371 // TODO: Support some PHYs combo settings?
1372
1373 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
1374 // Events on successful connection:
1375 // - HCI_LE_Enhanced_Connection_Complete
1376 // - HCI_LE_Channel_Selection_Algorithm
1377 } else {
1379 hci_cp_le_create_conn * cp = req0.getWStruct();
1380 cp->scan_interval = jau::cpu_to_le(le_scan_interval);
1381 cp->scan_window = jau::cpu_to_le(le_scan_window);
1382 cp->filter_policy = initiator_filter;
1383 cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type);
1384 cp->peer_addr = jau::cpu_to_le(peer_bdaddr);
1385 cp->own_address_type = static_cast<uint8_t>(own_mac_type);
1386 cp->conn_interval_min = jau::cpu_to_le(conn_interval_min);
1387 cp->conn_interval_max = jau::cpu_to_le(conn_interval_max);
1388 cp->conn_latency = jau::cpu_to_le(conn_latency);
1389 cp->supervision_timeout = jau::cpu_to_le(supervision_timeout);
1390 cp->min_ce_len = jau::cpu_to_le(min_ce_length);
1391 cp->max_ce_len = jau::cpu_to_le(max_ce_length);
1392
1393 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
1394 // Events on successful connection:
1395 // - HCI_LE_Connection_Complete
1396 }
1397 if( HCIStatusCode::SUCCESS != status ) {
1398 removeTrackerConnection(conn);
1399
1401 const std::string s0 = nullptr != disconn ? disconn->toString() : "null";
1402 WARN_PRINT("%s: disconnect pending: %s - %s",
1403 to_string(status).c_str(), s0.c_str(), toString().c_str());
1404 }
1405 }
1406 return status;
1407}
1408
1410 const uint16_t pkt_type,
1411 const uint16_t clock_offset, const uint8_t role_switch) noexcept {
1412 /**
1413 * As we rely on consistent 'pending tracker connections',
1414 * i.e. avoid a race condition on issuing connections via this command,
1415 * we need to synchronize this method.
1416 */
1417 const std::lock_guard<std::mutex> lock(mtx_connect_cmd); // RAII-style acquire and relinquish via destructor
1418
1419 if( !isOpen() ) {
1420 ERR_PRINT("Not connected %s", toString().c_str());
1422 }
1423 if( advertisingEnabled ) {
1424 WARN_PRINT("Not allowed: Advertising is enabled %s", toString().c_str());
1426 }
1427
1429 hci_cp_create_conn * cp = req0.getWStruct();
1430 cp->bdaddr = jau::cpu_to_le(bdaddr);
1431 cp->pkt_type = jau::cpu_to_le((uint16_t)(pkt_type & (uint16_t)ACL_PTYPE_MASK)); /* TODO OK excluding SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) ? */
1432 cp->pscan_rep_mode = 0x02; /* TODO magic? */
1433 cp->pscan_mode = 0x00; /* TODO magic? */
1434 cp->clock_offset = jau::cpu_to_le(clock_offset);
1435 cp->role_switch = role_switch;
1436
1437 size_type pendingConnections = countPendingTrackerConnections();
1438 if( 0 < pendingConnections ) {
1439 DBG_PRINT("HCIHandler<%u>::create_conn: %zu connections pending - %s", dev_id, (size_t)pendingConnections, toString().c_str());
1440 jau::fraction_i64 td = 0_s;
1441 while( env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT > td && 0 < pendingConnections ) {
1442 sleep_for(env.HCI_COMMAND_POLL_PERIOD);
1443 td += env.HCI_COMMAND_POLL_PERIOD;
1444 pendingConnections = countPendingTrackerConnections();
1445 }
1446 if( 0 < pendingConnections ) {
1447 WARN_PRINT("%zu connections pending after %" PRIi64 " ms - %s", (size_t)pendingConnections, td.to_ms(), toString().c_str());
1448 } else {
1449 DBG_PRINT("HCIHandler<%u>::create_conn: pending connections resolved after %" PRIi64 " ms - %s", dev_id, td.to_ms(), toString().c_str());
1450 }
1451 }
1452 const BDAddressAndType addressAndType(bdaddr, BDAddressType::BDADDR_BREDR);
1453 HCIConnectionRef disconn = findDisconnectCmd(addressAndType);
1454 if( nullptr != disconn ) {
1455 DBG_PRINT("HCIHandler<%u>::create_conn: disconnect pending %s - %s",
1456 dev_id, disconn->toString().c_str(), toString().c_str());
1457 jau::fraction_i64 td = 0_s;
1458 while( env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT > td && nullptr != disconn ) {
1459 sleep_for(env.HCI_COMMAND_POLL_PERIOD);
1460 td += env.HCI_COMMAND_POLL_PERIOD;
1461 disconn = findDisconnectCmd(addressAndType);
1462 }
1463 if( nullptr != disconn ) {
1464 WARN_PRINT("disconnect persisting after %" PRIi64 " ms: %s - %s",
1465 td.to_ms(), disconn->toString().c_str(), toString().c_str());
1466 } else {
1467 DBG_PRINT("HCIHandler<%u>::create_conn: disconnect resolved after %" PRIi64 " ms - %s", dev_id, td.to_ms(), toString().c_str());
1468 }
1469 }
1470 HCIConnectionRef conn = addOrUpdateTrackerConnection(addressAndType, 0);
1471 HCIStatusCode status;
1472 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
1473 if( HCIStatusCode::SUCCESS != status ) {
1474 removeTrackerConnection(conn);
1475
1477 const std::string s0 = nullptr != disconn ? disconn->toString() : "null";
1478 WARN_PRINT("%s: disconnect pending: %s - %s",
1479 to_string(status).c_str(), s0.c_str(), toString().c_str());
1480 }
1481 }
1482 return status;
1483}
1484
1485HCIStatusCode HCIHandler::disconnect(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType,
1486 const HCIStatusCode reason) noexcept
1487{
1488 HCIStatusCode status = check_open_connection("disconnect", conn_handle, peerAddressAndType, true /* addUntrackedConn */);
1489 if( HCIStatusCode::SUCCESS != status ) {
1490 return status;
1491 }
1492
1493 // Always issue DISCONNECT command, even in case of an ioError (lost-connection),
1494 // see Issue #124 fast re-connect on CSR adapter.
1495 // This will always notify the adapter of a disconnected device.
1496 {
1498 hci_cp_disconnect * cp = req0.getWStruct();
1499 cp->handle = jau::cpu_to_le(conn_handle);
1500 cp->reason = number(reason);
1501
1502 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
1503 }
1504 if( HCIStatusCode::SUCCESS == status ) {
1505 addOrUpdateDisconnectCmd(peerAddressAndType, conn_handle);
1506 }
1507 return status;
1508}
1509
1511 jau::uint128dp_t& peer_irk, jau::uint128dp_t& local_irk) noexcept {
1512 if( !use_resolv_add() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1513 HCIStatusCode status;
1515 hci_cp_le_add_to_resolv_list * cp = req0.getWStruct();
1516 cp->bdaddr_type = static_cast<uint8_t>(peerIdentityAddressAndType.type);
1517 cp->bdaddr = jau::cpu_to_le(peerIdentityAddressAndType.address);
1518 jau::put_uint128(cp->peer_irk + 0, peer_irk, jau::lb_endian_t::little);
1519 jau::put_uint128(cp->local_irk + 0, local_irk, jau::lb_endian_t::little);
1520 const hci_rp_status * ev_res;
1521 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1522 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1523 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1524 }
1525 return status;
1526}
1527
1528HCIStatusCode HCIHandler::le_del_from_resolv_list(const BDAddressAndType& peerIdentityAddressAndType) noexcept {
1529 if( !use_resolv_del() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1530 HCIStatusCode status;
1532 hci_cp_le_del_from_resolv_list * cp = req0.getWStruct();
1533 cp->bdaddr_type = static_cast<uint8_t>(peerIdentityAddressAndType.type);
1534 cp->bdaddr = jau::cpu_to_le(peerIdentityAddressAndType.address);
1535 const hci_rp_status * ev_res;
1536 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1537 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1538 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1539 }
1540 return status;
1541}
1542
1545 HCIStatusCode status;
1547 const hci_rp_status * ev_res;
1548 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1549 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1550 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1551 }
1552 return status;
1553}
1554
1556 if( !use_resolv_size() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1557 size_res = 0;
1558 HCIStatusCode status;
1560 const hci_rp_le_read_resolv_list_size * ev_res;
1561 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1562 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1563 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1564 } else if( nullptr != ev_res && HCIStatusCode::SUCCESS != status ) {
1565 size_res = ev_res->size;
1566 }
1567 return status;
1568}
1569
1571 jau::EUI48& peerResolvableAddress) noexcept {
1572 if( !use_resolv_readPeerRA() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1573 peerResolvableAddress.clear();
1574 HCIStatusCode status;
1576 hci_cp_le_read_peer_resolv_addr * cp = req0.getWStruct();
1577 cp->peer_id_addr_type = static_cast<uint8_t>(peerIdentityAddressAndType.type);
1578 cp->peer_id_addr = jau::cpu_to_le(peerIdentityAddressAndType.address);
1579 const hci_rp_le_read_peer_resolv_addr * ev_res;
1580 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1581 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1582 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1583 } else if( nullptr != ev_res && HCIStatusCode::SUCCESS != status ) {
1584 peerResolvableAddress = jau::le_to_cpu(ev_res->peer_resolv_addr);
1585 }
1586 return status;
1587}
1588
1590 jau::EUI48& localResolvableAddress) noexcept {
1591 if( !use_resolv_readLocalRA() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1592 localResolvableAddress.clear();
1593 HCIStatusCode status;
1595 hci_cp_le_read_local_resolv_addr * cp = req0.getWStruct();
1596 cp->peer_id_addr_type = static_cast<uint8_t>(peerIdentityAddressAndType.type);
1597 cp->peer_id_addr = jau::cpu_to_le(peerIdentityAddressAndType.address);
1598 const hci_rp_le_read_local_resolv_addr * ev_res;
1599 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1600 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1601 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1602 } else if( nullptr != ev_res && HCIStatusCode::SUCCESS != status ) {
1603 localResolvableAddress = jau::le_to_cpu(ev_res->local_resolv_addr);
1604 }
1605 return status;
1606}
1607
1609 if( !use_resolv_enable() ) { return HCIStatusCode::UNKNOWN_COMMAND; }
1610 HCIStatusCode status;
1612 hci_cp_le_set_addr_resolv_enable * cp = req0.getWStruct();
1613 cp->enable = enable ? 0x01 : 0x00;
1614 const hci_rp_status * ev_res;
1615 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_res, &status, true /* quiet */);
1616 if( nullptr == ev || nullptr == ev_res || HCIStatusCode::SUCCESS != status ) {
1617 DBG_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1618 }
1619 return status;
1620}
1621
1622HCIStatusCode HCIHandler::le_read_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType,
1623 LE_PHYs& resTx, LE_PHYs& resRx) noexcept {
1624 if( !is_set(le_ll_feats, LE_Features::LE_2M_PHY) ) {
1625 resTx = LE_PHYs::LE_1M;
1626 resRx = LE_PHYs::LE_1M;
1628 }
1629 resTx = LE_PHYs::NONE;
1630 resRx = LE_PHYs::NONE;
1631
1632 HCIStatusCode status = check_open_connection("le_read_phy", conn_handle, peerAddressAndType);
1633 if( HCIStatusCode::SUCCESS != status ) {
1634 return status;
1635 }
1636
1637 struct hci_cp_le_read_phy {
1638 __le16 handle;
1639 } __packed;
1640 struct hci_rp_le_read_phy {
1641 __u8 status;
1642 __le16 handle;
1643 __u8 tx_phys;
1644 __u8 rx_phys;
1645 } __packed;
1646
1648 hci_cp_le_read_phy * cp = req0.getWStruct();
1649 cp->handle = jau::cpu_to_le(conn_handle);
1650 const hci_rp_le_read_phy * ev_phy;
1651 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_phy, &status);
1652
1653 if( nullptr == ev || nullptr == ev_phy || HCIStatusCode::SUCCESS != status ) {
1654 ERR_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(),
1655 number(status), to_string(status).c_str(), toString().c_str());
1656 } else {
1657 const uint16_t conn_handle_rcvd = jau::le_to_cpu(ev_phy->handle);
1658 if( conn_handle != conn_handle_rcvd ) {
1659 ERR_PRINT("Mismatch given address%s conn_handle (req) %s != %s (res) (drop) - %s",
1660 peerAddressAndType.toString().c_str(),
1661 jau::to_hexstring(conn_handle).c_str(), jau::to_hexstring(conn_handle_rcvd).c_str(), toString().c_str());
1663 }
1664 switch( ev_phy->tx_phys ) {
1665 case 0x01: resTx = LE_PHYs::LE_1M; break;
1666 case 0x02: resTx = LE_PHYs::LE_2M; break;
1667 case 0x03: resTx = LE_PHYs::LE_CODED; break;
1668 }
1669 switch( ev_phy->rx_phys ) {
1670 case 0x01: resRx = LE_PHYs::LE_1M; break;
1671 case 0x02: resRx = LE_PHYs::LE_2M; break;
1672 case 0x03: resRx = LE_PHYs::LE_CODED; break;
1673 }
1674 }
1675 return status;
1676}
1677
1679 if( !is_set(le_ll_feats, LE_Features::LE_2M_PHY) ) {
1680 if( is_set(Tx, LE_PHYs::LE_2M) ) {
1681 WARN_PRINT("dev_id %u: LE_2M_PHY no supported, requested Tx %s", dev_id, to_string(Tx).c_str());
1683 }
1684 if( is_set(Rx, LE_PHYs::LE_2M) ) {
1685 WARN_PRINT("dev_id %u: LE_2M_PHY no supported, requested Rx %s", dev_id, to_string(Rx).c_str());
1687 }
1688 }
1689
1690 if( !isOpen() ) {
1691 ERR_PRINT("Not connected %s", toString().c_str());
1693 }
1694
1695 HCIStatusCode status;
1697 hci_cp_le_set_default_phy * cp = req0.getWStruct();
1698 cp->all_phys = ( Tx != LE_PHYs::NONE ? 0b000 : 0b001 ) |
1699 ( Rx != LE_PHYs::NONE ? 0b000 : 0b010 );
1700 cp->tx_phys = number( Tx );
1701 cp->rx_phys = number( Rx );
1702
1703 const hci_rp_status * ev_status;
1704 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1705
1706 if( nullptr == ev || nullptr == ev || HCIStatusCode::SUCCESS != status ) {
1707 ERR_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1708 }
1709 return status;
1710}
1711
1712HCIStatusCode HCIHandler::le_set_phy(const uint16_t conn_handle, const BDAddressAndType& peerAddressAndType,
1713 const LE_PHYs Tx, const LE_PHYs Rx) noexcept {
1714 if( !is_set(le_ll_feats, LE_Features::LE_2M_PHY) ) {
1715 if( is_set(Tx, LE_PHYs::LE_2M) ) {
1716 WARN_PRINT("dev_id %u: LE_2M_PHY no supported, requested Tx %s", dev_id, to_string(Tx).c_str());
1718 }
1719 if( is_set(Rx, LE_PHYs::LE_2M) ) {
1720 WARN_PRINT("dev_id %u: LE_2M_PHY no supported, requested Rx %s", dev_id, to_string(Rx).c_str());
1722 }
1723 }
1724
1725 HCIStatusCode status = check_open_connection("le_set_phy", conn_handle, peerAddressAndType);
1726 if( HCIStatusCode::SUCCESS != status ) {
1727 return status;
1728 }
1729
1730 struct hci_cp_le_set_phy {
1731 uint16_t handle;
1732 __u8 all_phys;
1733 __u8 tx_phys;
1734 __u8 rx_phys;
1735 uint16_t phy_options;
1736 } __packed;
1737
1739 hci_cp_le_set_phy * cp = req0.getWStruct();
1740 cp->handle = jau::cpu_to_le(conn_handle);
1741 cp->all_phys = ( Tx != LE_PHYs::NONE ? 0b000 : 0b001 ) |
1742 ( Rx != LE_PHYs::NONE ? 0b000 : 0b010 );
1743 cp->tx_phys = number( Tx );
1744 cp->rx_phys = number( Rx );
1745 cp->phy_options = 0;
1746
1747 std::unique_ptr<HCIEvent> ev = processCommandStatus(req0, &status);
1748
1749 if( nullptr == ev || nullptr == ev || HCIStatusCode::SUCCESS != status ) {
1750 ERR_PRINT("%s: 0x%x (%s) - %s", to_string(req0.getOpcode()).c_str(), number(status), to_string(status).c_str(), toString().c_str());
1751 }
1752 return status;
1753}
1754
1755HCIStatusCode HCIHandler::le_set_adv_param(const EUI48 &peer_bdaddr,
1756 const HCILEOwnAddressType own_mac_type,
1757 const HCILEOwnAddressType peer_mac_type,
1758 const uint16_t adv_interval_min, const uint16_t adv_interval_max,
1759 const AD_PDU_Type adv_type,
1760 const uint8_t adv_chan_map,
1761 const uint8_t filter_policy) noexcept {
1762 DBG_PRINT("HCIHandler<%u>::le_set_adv_param: adv-interval[%.3f ms .. %.3f ms], filter %d - %s",
1763 dev_id, 0.625f * (float)adv_interval_min, 0.625f * (float)adv_interval_max,
1764 filter_policy, toString().c_str());
1765
1766 HCIStatusCode status;
1767 if( use_ext_adv() ) {
1769 hci_cp_le_set_ext_adv_params * cp = req0.getWStruct();
1770 cp->handle = 0x00; // TODO: Support more than one advertising sets?
1771 AD_PDU_Type adv_type2;
1772 switch( adv_type ) {
1773 // Connectable
1775 [[fallthrough]];
1777 [[fallthrough]];
1779 adv_type2 = AD_PDU_Type::ADV_IND2;
1780 break;
1781
1782 // Non Connectable
1784 adv_type2 = AD_PDU_Type::SCAN_IND2;
1785 break;
1786
1788 [[fallthrough]];
1790 adv_type2 = AD_PDU_Type::NONCONN_IND2;
1791 break;
1792
1793 default:
1794 WARN_PRINT("dev_id %u: Invalid AD_PDU_Type %x (%s)", dev_id, adv_type, to_string(adv_type).c_str());
1796 }
1797 cp->evt_properties = number(adv_type2);
1798 // Actually .. but struct uses uint8_t[3] duh ..
1799 // cp->min_interval = jau::cpu_to_le(adv_interval_min);
1800 // cp->max_interval = jau::cpu_to_le(adv_interval_max);
1803 cp->channel_map = adv_chan_map;
1804 cp->own_addr_type = static_cast<uint8_t>(own_mac_type);
1805 cp->peer_addr_type = static_cast<uint8_t>(peer_mac_type);
1806 cp->peer_addr = jau::cpu_to_le(peer_bdaddr);
1807 cp->filter_policy = filter_policy;
1808 cp->tx_power = 0x7f; // Host has no preference (default); -128 to +20 [dBm]
1809 cp->primary_phy = direct_bt::number(LE_PHYs::LE_1M);
1810 // TODO: Support LE_1M + LE_CODED combo? Then must not use legacy PDU adv_type!
1811 // cp->secondary_max_skip;
1812 cp->secondary_phy = direct_bt::number(LE_PHYs::LE_1M);
1813 cp->sid = 0x00; // TODO: Support more than one advertising SID subfield?
1814 cp->notif_enable = 0x01;
1815 const hci_rp_le_set_ext_adv_params * ev_reply;
1816 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_reply, &status);
1817 // Not using `ev_reply->tx_power` yet.
1818 } else {
1820 hci_cp_le_set_adv_param * cp = req0.getWStruct();
1821 cp->min_interval = jau::cpu_to_le(adv_interval_min);
1822 cp->max_interval = jau::cpu_to_le(adv_interval_max);
1823 cp->type = number(adv_type);
1824 cp->own_address_type = static_cast<uint8_t>(own_mac_type);
1825 cp->direct_addr_type = static_cast<uint8_t>(peer_mac_type);
1826 cp->direct_addr = jau::cpu_to_le(peer_bdaddr);
1827 cp->channel_map = adv_chan_map;
1828 cp->filter_policy = filter_policy;
1829 const hci_rp_status * ev_status;
1830 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1831 }
1832 return status;
1833}
1834
1835
1836HCIStatusCode HCIHandler::le_set_adv_data(const EInfoReport &eir, const EIRDataType mask) noexcept {
1837 DBG_PRINT("HCIHandler<%u>::le_set_adv_data: %d", dev_id, eir.toString(true).c_str());
1838
1839 HCIStatusCode status;
1840 if( use_ext_adv() ) {
1841
1843 hci_cp_le_set_ext_adv_data * cp = req0.getWStruct();
1844 const jau::nsize_t max_data_len = HCI_MAX_AD_LENGTH; // not sizeof(cp->data), as we use legacy PDU
1845 cp->handle = 0x00; // TODO: Support more than one advertising sets?
1846 cp->operation = LE_SET_ADV_DATA_OP_COMPLETE;
1847 cp->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
1848 cp->length = eir.write_data(mask, cp->data, max_data_len);
1849 req0.trimParamSize( req0.getParamSize() + cp->length - sizeof(cp->data) );
1850
1851 const hci_rp_status * ev_status;
1852 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1853
1854 } else {
1855
1857 hci_cp_le_set_adv_data * cp = req0.getWStruct();
1858 cp->length = eir.write_data(mask, cp->data, sizeof(cp->data));
1859 // No param-size trimming for BT4, fixed 31 bytes
1860
1861 const hci_rp_status * ev_status;
1862 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1863
1864 }
1865 return status;
1866}
1867
1868HCIStatusCode HCIHandler::le_set_scanrsp_data(const EInfoReport &eir, const EIRDataType mask) noexcept {
1869 DBG_PRINT("HCIHandler<%u>::le_set_scanrsp_data: %d", dev_id, eir.toString(true).c_str());
1870
1871 HCIStatusCode status;
1872 if( use_ext_adv() ) {
1873
1875 hci_cp_le_set_ext_scan_rsp_data * cp = req0.getWStruct();
1876 const jau::nsize_t max_data_len = HCI_MAX_AD_LENGTH; // not sizeof(cp->data), as we use legacy PDU
1877 cp->handle = 0x00; // TODO: Support more than one advertising sets?
1878 cp->operation = LE_SET_ADV_DATA_OP_COMPLETE;
1879 cp->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
1880 cp->length = eir.write_data(mask, cp->data, max_data_len);
1881 req0.trimParamSize( req0.getParamSize() + cp->length - sizeof(cp->data) );
1882
1883 const hci_rp_status * ev_status;
1884 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1885
1886 } else {
1887
1889 hci_cp_le_set_scan_rsp_data * cp = req0.getWStruct();
1890 cp->length = eir.write_data(mask, cp->data, sizeof(cp->data));
1891 // No param-size trimming for BT4, fixed 31 bytes
1892
1893 const hci_rp_status * ev_status;
1894 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1895
1896 }
1897 return status;
1898}
1899
1900HCIStatusCode HCIHandler::le_enable_adv(const bool enable) noexcept {
1901 if( !isOpen() ) {
1902 ERR_PRINT("Not connected %s", toString().c_str());
1904 }
1905 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1906
1907 if( enable ) {
1908 if( ScanType::NONE != currentScanType ) {
1909 WARN_PRINT("Not allowed (scan enabled): %s", toString().c_str());
1911 }
1912 const size_type connCount = getTrackerConnectionCount();
1913 if( 0 < connCount ) {
1914 WARN_PRINT("Not allowed (%zu connections open/pending): %s", (size_t)connCount, toString().c_str());
1916 }
1917 }
1918 DBG_PRINT("HCIHandler<%u>::le_enable_adv: enable %d - %s", dev_id, enable, toString().c_str());
1919
1921
1922 if( use_ext_adv() ) {
1923 const hci_rp_status * ev_status;
1924 if( enable ) {
1925 struct hci_cp_le_set_ext_adv_enable_1 {
1926 __u8 enable;
1927 __u8 num_of_sets;
1928 hci_cp_ext_adv_set sets[1];
1929 } __packed;
1930
1932 hci_cp_le_set_ext_adv_enable_1 * cp = req0.getWStruct();
1933 cp->enable = 0x01;
1934 cp->num_of_sets = 1;
1935 cp->sets[0].handle = 0x00;
1936 cp->sets[0].duration = 0; // continue adv until host disables
1937 cp->sets[0].max_events = 0; // no maximum number of adv events
1938 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1939 } else {
1941 hci_cp_le_set_ext_adv_enable * cp = req0.getWStruct();
1942 cp->enable = 0x00;
1943 cp->num_of_sets = 0; // disable all advertising sets
1944 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1945 }
1946 } else {
1947 struct hci_cp_le_set_adv_enable {
1948 uint8_t enable;
1949 } __packed;
1951 hci_cp_le_set_adv_enable * cp = req0.getWStruct();
1952 cp->enable = enable ? 0x01 : 0x00;
1953 const hci_rp_status * ev_status;
1954 std::unique_ptr<HCIEvent> ev = processCommandComplete(req0, &ev_status, &status);
1955 }
1956 if( HCIStatusCode::SUCCESS == status ) {
1957 advertisingEnabled = enable;
1958 } else if( advertisingEnabled == enable ) {
1959 // Override erroneous HCI failure when
1960 // - disabling advertising when already disabled, or
1961 // - enabling advertising when already enabled
1962 // as stated in spec 'BT Core Spec v5.2: Vol 4 HCI, Part E HCI Functional: 7.8.9 LE Set Advertising Enable command'
1963 WARN_PRINT("enable-arg %d == enabled-state %d: Unchanged request, overriding failure: %s -> %s - %s",
1964 enable, advertisingEnabled.load(), to_string(status).c_str(), to_string(HCIStatusCode::SUCCESS).c_str(), toString().c_str());
1965 status = HCIStatusCode::SUCCESS;
1966 }
1967 return status;
1968}
1969
1971 const EIRDataType adv_mask, const EIRDataType scanrsp_mask,
1972 const EUI48 &peer_bdaddr,
1973 const HCILEOwnAddressType own_mac_type,
1974 const HCILEOwnAddressType peer_mac_type,
1975 const uint16_t adv_interval_min, const uint16_t adv_interval_max,
1976 const AD_PDU_Type adv_type,
1977 const uint8_t adv_chan_map,
1978 const uint8_t filter_policy) noexcept
1979{
1980 if( !isOpen() ) {
1981 ERR_PRINT("Not connected %s", toString().c_str());
1983 }
1984 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
1985
1986 if( ScanType::NONE != currentScanType ) {
1987 WARN_PRINT("Not allowed (scan enabled): %s", toString().c_str());
1989 }
1990 const size_type connCount = getTrackerConnectionCount();
1991 if( 0 < connCount ) {
1992 WARN_PRINT("Not allowed (%zu connections open/pending): %s", (size_t)connCount, toString().c_str());
1994 }
1995
1996 HCIStatusCode status = le_set_adv_data(eir, adv_mask);
1997 if( HCIStatusCode::SUCCESS != status ) {
1998 WARN_PRINT("le_set_adv_data: %s - %s",
1999 to_string(status).c_str(), toString().c_str());
2000 return status;
2001 }
2002
2003 status = le_set_scanrsp_data(eir, scanrsp_mask);
2004 if( HCIStatusCode::SUCCESS != status ) {
2005 WARN_PRINT("le_set_scanrsp_data: %s - %s",
2006 to_string(status).c_str(), toString().c_str());
2007 return status;
2008 }
2009 status = le_set_adv_param(peer_bdaddr, own_mac_type, peer_mac_type,
2012 if( HCIStatusCode::SUCCESS != status ) {
2013 WARN_PRINT("le_set_adv_param: %s - %s",
2014 to_string(status).c_str(), toString().c_str());
2015 return status;
2016 }
2017
2018 status = le_enable_adv(true);
2019 if( HCIStatusCode::SUCCESS != status ) {
2020 WARN_PRINT("le_enable_adv failed: %s - %s",
2021 to_string(status).c_str(), toString().c_str());
2022 }
2023
2024 return status;
2025}
2026
2027std::unique_ptr<HCIEvent> HCIHandler::processCommandStatus(HCICommand &req, HCIStatusCode *status, const bool quiet) noexcept
2028{
2029 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
2030
2032
2033 int32_t retryCount = 0;
2034 std::unique_ptr<HCIEvent> ev = nullptr;
2035
2036 if( !sendCommand(req) ) {
2037 goto exit;
2038 }
2039
2040 while( retryCount < env.HCI_READ_PACKET_MAX_RETRY ) {
2041 ev = getNextReply(req, retryCount, env.HCI_COMMAND_STATUS_REPLY_TIMEOUT);
2042 if( nullptr == ev ) {
2044 break; // timeout, leave loop
2045 } else if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
2046 HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
2047 *status = ev_cs->getStatus();
2048 DBG_PRINT("HCIHandler<%u>::processCommandStatus %s -> Status 0x%2.2X (%s), errno %d %s: res %s, req %s - %s",
2049 dev_id, to_string(req.getOpcode()).c_str(),
2050 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2051 ev_cs->toString().c_str(), req.toString().c_str(), toString().c_str());
2052 break; // gotcha, leave loop - pending completion result handled via callback
2053 } else {
2054 retryCount++;
2055 DBG_PRINT("HCIHandler<%u>::processCommandStatus: !CMD_STATUS (drop, retry %d): res %s; req %s - %s",
2056 dev_id, retryCount, ev->toString().c_str(), req.toString().c_str(), toString().c_str());
2057 continue; // next packet
2058 }
2059 }
2060 if( nullptr == ev ) {
2061 // timeout exit
2062 if( !quiet || jau::environment::get().verbose ) {
2063 WARN_PRINT("%s -> Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s - %s",
2064 to_string(req.getOpcode()).c_str(),
2065 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2066 req.toString().c_str(), toString().c_str());
2067 }
2068 }
2069
2070exit:
2071 return ev;
2072}
2073
2074template<typename hci_cmd_event_struct>
2075std::unique_ptr<HCIEvent> HCIHandler::processCommandComplete(HCICommand &req,
2076 const hci_cmd_event_struct **res, HCIStatusCode *status,
2077 const bool quiet) noexcept
2078{
2079 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
2080
2081 *res = nullptr;
2083
2084 if( !sendCommand(req, quiet) ) {
2085 if( !quiet || jau::environment::get().verbose ) {
2086 WARN_PRINT("Send failed: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s - %s",
2087 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2088 req.toString().c_str(), toString().c_str());
2089 }
2090 return nullptr; // timeout
2091 }
2092
2093 return receiveCommandComplete(req, res, status, quiet);
2094}
2095
2096template<typename hci_cmd_event_struct>
2097std::unique_ptr<HCIEvent> HCIHandler::receiveCommandComplete(HCICommand &req,
2098 const hci_cmd_event_struct **res, HCIStatusCode *status,
2099 const bool quiet) noexcept
2100{
2101 *res = nullptr;
2103
2106 std::unique_ptr<HCIEvent> ev = getNextCmdCompleteReply(req, &ev_cc);
2107 if( nullptr == ev ) {
2109 if( !quiet || jau::environment::get().verbose ) {
2110 WARN_PRINT("%s -> %s: Status 0x%2.2X (%s), errno %d %s: res nullptr, req %s - %s",
2111 to_string(req.getOpcode()).c_str(), to_string(evc).c_str(),
2112 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2113 req.toString().c_str(), toString().c_str());
2114 }
2115 return nullptr; // timeout
2116 } else if( nullptr == ev_cc ) {
2117 if( ev->isEvent(HCIEventType::CMD_STATUS) ) {
2118 HCICommandStatusEvent * ev_cs = static_cast<HCICommandStatusEvent*>(ev.get());
2119 *status = ev_cs->getStatus();
2120 }
2121 if( !quiet || jau::environment::get().verbose ) {
2122 WARN_PRINT("%s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s - %s",
2123 to_string(req.getOpcode()).c_str(), to_string(evc).c_str(),
2124 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2125 ev->toString().c_str(), req.toString().c_str(), toString().c_str());
2126 }
2127 return ev;
2128 }
2129 const uint8_t returnParamSize = ev_cc->getReturnParamSize();
2130 if( returnParamSize < sizeof(hci_cmd_event_struct) ) {
2131 if( !quiet || jau::environment::get().verbose ) {
2132 WARN_PRINT("%s -> %s: Status 0x%2.2X (%s), errno %d %s: res %s, req %s - %s",
2133 to_string(req.getOpcode()).c_str(), to_string(evc).c_str(),
2134 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2135 ev_cc->toString().c_str(), req.toString().c_str(), toString().c_str());
2136 }
2137 return ev;
2138 }
2139 *res = (const hci_cmd_event_struct*)(ev_cc->getReturnParam());
2140 *status = static_cast<HCIStatusCode>((*res)->status);
2141 DBG_PRINT("HCIHandler<%u>::receiveCommandComplete %s -> %s: Status 0x%2.2X (%s): res %s, req %s - %s",
2142 dev_id, to_string(req.getOpcode()).c_str(), to_string(evc).c_str(),
2143 number(*status), to_string(*status).c_str(),
2144 ev_cc->toString().c_str(), req.toString().c_str(), toString().c_str());
2145 return ev;
2146}
2147
2148template<typename hci_cmd_event_struct>
2149const hci_cmd_event_struct* HCIHandler::getReplyStruct(HCIEvent& event, HCIEventType evc, HCIStatusCode *status) noexcept
2150{
2151 const hci_cmd_event_struct* res = nullptr;
2153
2154 typedef HCIStructCmdCompleteEvtWrap<hci_cmd_event_struct> HCITypeCmdCompleteEvtWrap;
2155 HCITypeCmdCompleteEvtWrap ev_cc( event );
2156 if( ev_cc.isTypeAndSizeValid(evc) ) {
2157 *status = ev_cc.getStatus();
2158 res = ev_cc.getStruct();
2159 } else {
2160 WARN_PRINT("%s: Type or size mismatch: Status 0x%2.2X (%s), errno %d %s: res %s - %s",
2161 to_string(evc).c_str(),
2162 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2163 ev_cc.toString().c_str(), toString().c_str());
2164 }
2165 return res;
2166}
2167
2168template<typename hci_cmd_event_struct>
2169const hci_cmd_event_struct* HCIHandler::getMetaReplyStruct(HCIEvent& event, HCIMetaEventType mec, HCIStatusCode *status) noexcept
2170{
2171 const hci_cmd_event_struct* res = nullptr;
2173
2174 typedef HCIStructCmdCompleteMetaEvtWrap<hci_cmd_event_struct> HCITypeCmdCompleteMetaEvtWrap;
2175 const HCITypeCmdCompleteMetaEvtWrap ev_cc( *static_cast<HCIMetaEvent*>( &event ) );
2176 if( ev_cc.isTypeAndSizeValid(mec) ) {
2177 *status = ev_cc.getStatus();
2178 res = ev_cc.getStruct();
2179 } else {
2180 WARN_PRINT("%s: Type or size mismatch: Status 0x%2.2X (%s), errno %d %s: res %s - %s",
2181 to_string(mec).c_str(),
2182 number(*status), to_string(*status).c_str(), errno, strerror(errno),
2183 ev_cc.toString().c_str(), toString().c_str());
2184 }
2185 return res;
2186}
2187
2188/***
2189 *
2190 * MgmtEventCallback section
2191 *
2192 */
2193
2195 [](const MgmtEventCallback &a, const MgmtEventCallback &b) -> bool { return a == b; };
2196
2198 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
2199 ERR_PRINT("Opcode %s >= %d - %s", MgmtEvent::getOpcodeString(opc).c_str(), mgmtEventCallbackLists.size(), toString().c_str());
2200 return false;
2201 }
2202 MgmtEventCallbackList &l = mgmtEventCallbackLists[static_cast<uint16_t>(opc)];
2203 /* const bool added = */ l.push_back_unique(cb, _mgmtEventCallbackEqComparator);
2204 return true;
2205}
2207 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
2208 ERR_PRINT("Opcode %s >= %d - %s", MgmtEvent::getOpcodeString(opc).c_str(), mgmtEventCallbackLists.size(), toString().c_str());
2209 return 0;
2210 }
2211 MgmtEventCallbackList &l = mgmtEventCallbackLists[static_cast<uint16_t>(opc)];
2212 return l.erase_matching(cb, true /* all_matching */, _mgmtEventCallbackEqComparator);
2213}
2215 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
2216 ERR_PRINT("Opcode %s >= %d - %s", MgmtEvent::getOpcodeString(opc).c_str(), mgmtEventCallbackLists.size(), toString().c_str());
2217 return;
2218 }
2219 mgmtEventCallbackLists[static_cast<uint16_t>(opc)].clear();
2220}
2222 for(auto & mgmtEventCallbackList : mgmtEventCallbackLists) {
2223 mgmtEventCallbackList.clear();
2224 }
2225 hciSMPMsgCallbackList.clear();
2226}
2227
2228/**
2229 * SMPMsgCallback handling
2230 */
2231
2233 [](const HCISMPMsgCallback& a, const HCISMPMsgCallback& b) -> bool { return a == b; };
2234
2235
2237 hciSMPMsgCallbackList.push_back(l);
2238}
2240 return hciSMPMsgCallbackList.erase_matching(l, true /* all_matching */, _changedHCISMPMsgCallbackEqComp);
2241}
2242
2243
static HCISMPMsgCallbackList::equal_comparator _changedHCISMPMsgCallbackEqComp
SMPMsgCallback handling.
static MgmtEventCallbackList::equal_comparator _mgmtEventCallbackEqComparator
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:175
std::string toString() const noexcept
Definition: BTTypes0.cpp:186
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
Definition: BTTypes0.hpp:898
static jau::darray< std::unique_ptr< EInfoReport > > read_ext_ad_reports(uint8_t const *data, jau::nsize_t const data_length) noexcept
Reads a complete Advertising Data (AD) Report and returns the number of AD reports in form of a shara...
Definition: BTTypes0.cpp:1435
static std::unique_ptr< HCIACLData > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
Definition: HCITypes.cpp:404
void close() noexcept
Closing the HCI channel, locking mutex_write().
Definition: HCIComm.cpp:118
static void filter_set_opcode(uint16_t opcode, hci_ufilter *f) noexcept
Definition: HCIComm.hpp:167
int socket() const noexcept
Return this HCI socket descriptor.
Definition: HCIComm.hpp:103
static void filter_all_events(hci_ufilter *f) noexcept
Definition: HCIComm.hpp:163
static void filter_set_event(int e, hci_ufilter *f) noexcept
Definition: HCIComm.hpp:151
static void filter_clear(hci_ufilter *f) noexcept
Definition: HCIComm.hpp:131
static void filter_set_ptype(int t, hci_ufilter *f) noexcept
Definition: HCIComm.hpp:135
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.14 Command Complete event.
Definition: HCITypes.hpp:1255
jau::nsize_t getReturnParamSize() const noexcept
Definition: HCITypes.hpp:1285
const uint8_t * getReturnParam() const
Definition: HCITypes.hpp:1286
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.15 Command Status event.
Definition: HCITypes.hpp:1303
HCIStatusCode getStatus() const noexcept
Definition: HCITypes.hpp:1321
BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.1 HCI Command packet.
Definition: HCITypes.hpp:616
static std::unique_ptr< HCICommand > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
Definition: HCITypes.cpp:321
HCIOpcode getOpcode() const noexcept
Definition: HCITypes.hpp:691
static HCIEnv & get() noexcept
Definition: HCIHandler.hpp:150
BT Core Spec v5.2: Vol 4, Part E HCI: 5.4.4 HCI Event packet.
Definition: HCITypes.hpp:1088
static std::unique_ptr< HCIEvent > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
Definition: HCITypes.cpp:351
bool use_ext_adv() const noexcept
Use extended advertising if LE_Features::LE_Ext_Adv is set (Bluetooth 5.0).
Definition: HCIHandler.hpp:429
bool use_ext_conn() const noexcept
Use extended connection if HCI_LE_Extended_Create_Connection is supported (Bluetooth 5....
Definition: HCIHandler.hpp:416
bool use_resolv_clear() const noexcept
Definition: HCIHandler.hpp:422
bool isOpen() const noexcept
Returns true if this mgmt instance is open, connected and hence valid, otherwise false.
Definition: HCIHandler.hpp:405
std::shared_ptr< HCIConnection > HCIConnectionRef
Definition: HCIHandler.hpp:237
HCIStatusCode le_clear_resolv_list() noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.40 LE Clear Resolving List command.
HCIStatusCode le_read_remote_features(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType) noexcept
Request supported LE_Features from remote device.
Definition: HCIHandler.cpp:932
void close() noexcept
Definition: HCIHandler.cpp:949
HCIStatusCode le_enable_adv(const bool enable) noexcept
Enables or disabled advertising.
HCIStatusCode le_enable_scan(const bool enable, const bool filter_dup=true) noexcept
Starts or stops LE scanning.
HCIStatusCode resetAdapter(const PostShutdownFunc &user_post_shutdown)
Complete adapter reset.
void clearAllCallbacks() noexcept
Removes all MgmtEventCallbacks from all MgmtEvent::Opcode lists and all SMPSecurityReqCallbacks.
bool addMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
MgmtEventCallback handling
HCIStatusCode le_read_resolv_list_size(uint32_t &size_res) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.41 LE Read Resolving List Size command.
void clearMgmtEventCallbacks(const MgmtEvent::Opcode opc) noexcept
Removes all MgmtEventCallbacks from the to the named MgmtEvent::Opcode list.
HCIStatusCode create_conn(const EUI48 &bdaddr, const uint16_t pkt_type=HCI_DM1|HCI_DM3|HCI_DM5|HCI_DH1|HCI_DH3|HCI_DH5, const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01) noexcept
Establish a connection to the given BREDR (non LE).
jau::nsize_t size_type
Definition: HCIHandler.hpp:187
HCIStatusCode le_set_phy(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets preference of used LE_PHYs for the given connection.
HCIStatusCode le_start_adv(const EInfoReport &eir, const EIRDataType adv_mask=EIRDataType::FLAGS|EIRDataType::SERVICE_UUID, const EIRDataType scanrsp_mask=EIRDataType::NAME|EIRDataType::CONN_IVAL, const EUI48 &peer_bdaddr=EUI48::ANY_DEVICE, const HCILEOwnAddressType own_mac_type=HCILEOwnAddressType::PUBLIC, const HCILEOwnAddressType peer_mac_type=HCILEOwnAddressType::PUBLIC, const uint16_t adv_interval_min=160, const uint16_t adv_interval_max=480, const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND, const uint8_t adv_chan_map=0x07, const uint8_t filter_policy=0x00) noexcept
Starts advertising.
HCIStatusCode le_read_phy(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, LE_PHYs &resTx, LE_PHYs &resRx) noexcept
Request and return LE_PHYs bit for the given connection.
HCIStatusCode le_set_default_phy(const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets default preference of used LE_PHYs for all subsequent LE connections.
HCIStatusCode le_start_scan(const bool filter_dup=true, const bool le_scan_active=true, const HCILEOwnAddressType own_mac_type=HCILEOwnAddressType::PUBLIC, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00) noexcept
Start LE scanning, i.e.
bool resetAllStates(const bool powered_on) noexcept
Reset all internal states, i.e.
size_type removeMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
Returns count of removed given MgmtEventCallback from the named MgmtEvent::Opcode list.
void sendMgmtEvent(const MgmtEvent &event) noexcept
Manually send a MgmtEvent to all of its listeners.
Definition: HCIHandler.cpp:588
HCIStatusCode le_add_to_resolv_list(const BDAddressAndType &peerIdentityAddressAndType, jau::uint128dp_t &peer_irk, jau::uint128dp_t &local_irk) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.38 LE Add Device To Resolving List command.
HCIStatusCode getLocalVersion(HCILocalVersion &version) noexcept
HCIStatusCode le_set_addr_resolv_enable(const bool enable) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.44 LE Set Address Resolution Enable command.
HCIStatusCode le_read_peer_resolv_addr(const BDAddressAndType &peerIdentityAddressAndType, jau::EUI48 &peerResolvableAddress) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.42 LE Read Peer Resolvable Address command.
void addSMPMsgCallback(const HCISMPMsgCallback &l)
std::string toString() const noexcept
Definition: HCIHandler.cpp:976
HCIStatusCode le_del_from_resolv_list(const BDAddressAndType &peerIdentityAddressAndType) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.39 LE Remove Device From Resolving List command.
HCIStatusCode le_create_conn(const EUI48 &peer_bdaddr, const HCILEPeerAddressType peer_mac_type=HCILEPeerAddressType::PUBLIC, const HCILEOwnAddressType own_mac_type=HCILEOwnAddressType::PUBLIC, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint16_t conn_interval_min=8, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15)) noexcept
Establish a connection to the given LE peer.
HCIStatusCode disconnect(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect an established connection.
HCIStatusCode le_read_local_resolv_addr(const BDAddressAndType &peerIdentityAddressAndType, jau::EUI48 &localResolvableAddress) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.43 LE Read Local Resolvable Address command.
HCIHandler(const uint16_t dev_id, const BTMode btMode=BTMode::NONE) noexcept
Definition: HCIHandler.cpp:686
size_type removeSMPMsgCallback(const HCISMPMsgCallback &l)
bool use_ext_scan() const noexcept
Use extended scanning if HCI_LE_Set_Extended_Scan_Parameters and HCI_LE_Set_Extended_Scan_Enable is s...
Definition: HCIHandler.hpp:410
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.24 LE Enable Encryption command.
Definition: HCITypes.hpp:775
constexpr jau::uint128dp_t getLTK() const noexcept
Returns the 128-bit Long Term Key (16 octets)
Definition: HCITypes.hpp:828
constexpr uint16_t getEDIV() const noexcept
Returns the 16-bit EDIV value (2 octets) being distributed.
Definition: HCITypes.hpp:819
constexpr uint64_t getRand() const noexcept
Returns the 64-bit Rand value (8 octets) being distributed.
Definition: HCITypes.hpp:811
constexpr uint16_t getHandle() const noexcept
Definition: HCITypes.hpp:803
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.25 LE Long Term Key Request Reply command.
Definition: HCITypes.hpp:850
constexpr uint16_t getHandle() const noexcept
Definition: HCITypes.hpp:873
constexpr jau::uint128dp_t getLTK() const noexcept
Returns the 128-bit Long Term Key (16 octets)
Definition: HCITypes.hpp:882
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.26 LE Long Term Key Request Negative Reply command.
Definition: HCITypes.hpp:898
constexpr uint16_t getHandle() const noexcept
Definition: HCITypes.hpp:920
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65.5 LE Long Term Key Request event.
Definition: HCITypes.hpp:1427
constexpr uint64_t getRand() const noexcept
Returns the 64-bit Rand value (8 octets) being distributed.
Definition: HCITypes.hpp:1454
constexpr uint16_t getHandle() const noexcept
Definition: HCITypes.hpp:1446
constexpr uint16_t getEDIV() const noexcept
Returns the 16-bit EDIV value (2 octets) being distributed.
Definition: HCITypes.hpp:1462
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event.
Definition: HCITypes.hpp:1353
std::string toString() const noexcept
Definition: HCITypes.hpp:597
Generic HCIEvent wrapper for any HCI IOCTL 'command complete' alike event struct having a HCIStatusCo...
Definition: HCITypes.hpp:1187
Generic HCIMetaEvent wrapper for any HCI IOCTL 'command complete' alike meta event struct having a HC...
Definition: HCITypes.hpp:1471
Generic HCICommand wrapper for any HCI IOCTL structure.
Definition: HCITypes.hpp:929
hcistruct * getWStruct() noexcept
Definition: HCITypes.hpp:948
uint16_t opcode, uint16_t dev-id, uint16_t param_size
Definition: MgmtTypes.hpp:1402
static std::string getOpcodeString(const Opcode opc) noexcept
Definition: MgmtTypes.cpp:401
mgmt_addr_info { EUI48, uint8_t type }, int8_t rssi, uint32_t flags, uint16_t eir_len; uint8_t *eir
Definition: MgmtTypes.hpp:2129
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:426
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: octets.hpp:162
constexpr uint8_t const * get_ptr() const noexcept
Definition: octets.hpp:272
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
Definition: cow_darray.hpp:127
constexpr_atomic void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: cow_darray.hpp:811
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:752
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:989
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:717
constexpr_atomic bool push_back_unique(const value_type &x, equal_comparator comparator)
Like std::vector::push_back(), but only if the newly added element does not yet exist.
constexpr_atomic size_type erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:148
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
Main jau environment class, supporting environment variable access and fetching elapsed time using it...
Definition: environment.hpp:74
const bool verbose
Verbose info logging enabled or disabled.
static environment & get(const std::string &root_prefix_domain="jau") noexcept
Static singleton initialization of this project's environment with the given global root prefix_domai...
constexpr int_type to_ms() const noexcept
Convenient shortcut to to_num_of(1_ms)
Class template jau::function is a general-purpose static-polymorphic function wrapper.
Service runner, a reusable dedicated thread performing custom user services.
bool stop() noexcept
Stops this service, if running.
bool shall_stop2(int dummy) noexcept
Helper function to easy FunctionDef usage w/o creating a lambda alike capture with same semantics as ...
bool join() noexcept
Blocks the current thread until service is stopped or returns immediately if not running or called fr...
static const uint8_t filter_policy
static const uint16_t le_scan_interval
static const uint16_t le_scan_window
static const bool filter_dup
static bool le_scan_active
static BTMode btMode
static const uint16_t adv_interval_max
static const uint8_t adv_chan_map
static const uint16_t adv_interval_min
static const AD_PDU_Type adv_type
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:151
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:68
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.hpp:101
#define PERF_TS_T0()
Definition: debug.hpp:79
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:112
#define DBG_WARN_PRINT(...)
Use for environment-variable environment::DEBUG conditional warning messages, prefix '[elapsed_time] ...
Definition: debug.hpp:55
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
Definition: debug.hpp:123
#define PERF_TS_TD(m)
Definition: debug.hpp:80
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
constexpr uint16_t le_to_cpu(uint16_t const l) noexcept
Definition: byte_util.hpp:396
constexpr void put_uint16(uint8_t *buffer, const uint16_t v) noexcept
Put the given uint16_t value into the given byte address using packed_t to resolve a potential memory...
Definition: byte_util.hpp:638
constexpr void put_uint128(uint8_t *buffer, const uint128dp_t &v) noexcept
See put_uint16() for reference.
Definition: byte_util.hpp:749
constexpr uint16_t cpu_to_le(uint16_t const h) noexcept
Definition: byte_util.hpp:405
constexpr uint64_t get_uint64(uint8_t const *buffer) noexcept
See get_uint16() for reference.
Definition: byte_util.hpp:732
@ little
Identifier for little endian, equivalent to endian::little.
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
#define __packed
packed: lead out macro, requires packed lead in as well.
HCIOpcode
BT Core Spec v5.2: Vol 4, Part E HCI: 7.1 Link Controller commands.
Definition: HCITypes.hpp:404
constexpr const bool CONSIDER_HCI_CMD_FOR_SMP_STATE
Definition: DBTConst.hpp:62
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
Definition: DBTConst.hpp:73
HCIEventType
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7 Events.
Definition: HCITypes.hpp:308
HCIMetaEventType
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65 LE Meta event.
Definition: HCITypes.hpp:350
@ LE_CREATE_CONN
LE_CREATE_CONN.
@ LE_READ_PEER_RESOLV_ADDR
FIXME: May not be supported by Linux/BlueZ.
@ LE_READ_LOCAL_RESOLV_ADDR
FIXME: May not be supported by Linux/BlueZ.
@ LE_EXT_CONN_COMPLETE
LE_ENHANCED_CONN_COMPLETE.
@ LE_CONN_COMPLETE
LE_CONN_COMPLETE.
@ LE_LTK_REQUEST
LE_LTK_REQUEST.
@ LE_ADVERTISING_REPORT
LE_ADVERTISING_REPORT.
@ LE_EXT_ADV_REPORT
LE_EXT_ADV_REPORT.
@ LE_REMOTE_FEAT_COMPLETE
LE_REMOTE_FEAT_COMPLETE.
@ LE_PHY_UPDATE_COMPLETE
LE_PHY_UPDATE_COMPLETE.
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
void clear() noexcept
Clears internal list.
LE_Features
HCI Supported Commands.
Definition: BTTypes0.hpp:162
constexpr ScanType changeScanType(const ScanType current, const ScanType changeType, const bool changeEnable) noexcept
Definition: BTTypes0.hpp:377
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
LE_PHYs
LE Transport PHY bit values.
Definition: BTTypes0.hpp:231
HCILEPeerAddressType
HCI LE Address-Type is PUBLIC: 0x00, RANDOM: 0x01.
Definition: BTAddress.hpp:135
ScanType
Meta ScanType as derived from BTMode, with defined value mask consisting of BDAddressType bits.
Definition: BTTypes0.hpp:350
AD_PDU_Type
LE Advertising (AD) Protocol Data Unit (PDU) Types.
Definition: BTTypes0.hpp:397
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
constexpr BDAddressType to_BDAddressType(const uint8_t v) noexcept
Definition: BTAddress.hpp:70
constexpr bool is_set(const LE_Features mask, const LE_Features bit) noexcept
Definition: BTTypes0.hpp:219
constexpr uint8_t number(const DiscoveryPolicy rhs) noexcept
Definition: BTAdapter.hpp:100
EIRDataType
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: BTTypes0.hpp:838
@ BDADDR_BREDR
Bluetooth BREDR address.
@ NONCONN_IND2
EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_NONCONN_IND variant.
@ ADV_NONCONN_IND
Non connectable devices, advertising information to any listening device.
@ SCAN_IND2
EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_SCAN_IND variant.
@ ADV_SCAN_IND
Similar to ADV_IND, w/o connection requests and with the option additional information via scan respo...
@ ADV_IND2
EAD_Event_Type with EAD_Event_Type::LEGACY_PDU: ADV_IND variant.
@ ADV_IND
Advertising Indications (ADV_IND), where a peripheral device requests connection to any central devic...
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
std::string bytesHexString(const void *data, const nsize_t offset, const nsize_t length, const bool lsbFirst, const bool lowerCase=true) noexcept
Produce a hexadecimal string representation of the given byte values.
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
uint8_t __u8
uint16_t __le16
constexpr const jau::fraction_i64 zero(0l, 1lu)
zero is 0/1
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
Representing ACL Datas' L2CAP Frame.
Definition: HCITypes.hpp:977
static std::string toString(const PBFlag v) noexcept
Definition: HCITypes.cpp:394
constexpr bool isGATT() const noexcept
Definition: HCITypes.hpp:1014
const uint16_t handle
The connection handle.
Definition: HCITypes.hpp:998
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
CXX_ALWAYS_INLINE bool compare_exchange_strong(_Tp &__e, _Tp __i) noexcept
A 128-bit packed uint8_t data array.
Definition: int_types.hpp:114