37 #include <sys/socket.h>
51#include "L2CAPIoctl.hpp"
62SMPEnv::SMPEnv() noexcept
64 SMP_READ_COMMAND_REPLY_TIMEOUT(
jau::
environment::getInt32Property("
direct_bt.smp.cmd.read.timeout", 500, 250 , INT32_MAX ) ),
65 SMP_WRITE_COMMAND_REPLY_TIMEOUT(
jau::
environment::getInt32Property("
direct_bt.smp.cmd.write.timeout", 500, 250 , INT32_MAX ) ),
74 std::shared_ptr<BTDevice> ref = wbr_device.lock();
75 if(
nullptr == ref ) {
81bool SMPHandler::validateConnected() noexcept {
82 bool l2capIsConnected = l2cap.
is_open();
85 if( has_ioerror || l2capHasIOError ) {
87 ERR_PRINT(
"ioerr state: GattHandler %s, l2cap %s: %s",
92 if( !is_connected || !l2capIsConnected ) {
93 ERR_PRINT(
"Disconnected state: GattHandler %s, l2cap %s: %s",
102 if( !validateConnected() ) {
103 ERR_PRINT(
"SMPHandler::reader: Invalid IO state -> Stop");
108 len = l2cap.read(rbuffer.get_wptr(), rbuffer.size());
114 COND_PRINT(env.DEBUG_DATA,
"SMPHandler-IO RECV (SEC_REQ) %s", smpPDU->toString().c_str());
119 COND_PRINT(env.DEBUG_DATA,
"SMPHandler-IO RECV (MSG) %s", smpPDU->toString().c_str());
120 if( smpPDURing.isFull() ) {
122 smpPDURing.drop(dropCount);
123 WARN_PRINT(
"SMPHandler-IO RECV Drop (%u oldest elements of %u capacity, ring full)", dropCount, smpPDURing.capacity());
125 if( !smpPDURing.putBlocking( std::move(smpPDU), 0_s ) ) {
126 ERR_PRINT2(
"smpPDURing put: %s", smpPDURing.toString().c_str());
132 WORDY_PRINT(
"SMPHandler::reader: l2cap read: IRQed res %d (%s); %s",
134 if( !sr.shall_stop() ) {
141 IRQ_PRINT(
"SMPHandler::reader: l2cap read: Error res %d (%s); %s",
146 WORDY_PRINT(
"SMPHandler::reader: l2cap read: Zero res %d (%s); %s",
154 WORDY_PRINT(
"SMPHandler::reader: Ended. Ring has %u entries flushed", smpPDURing.size());
161 if(
nullptr != device ) {
171 wbr_device(device), deviceString(device->getAddressAndType().toString()),
174 is_connected(l2cap.open(*device)), has_ioerror(
false),
179 smpPDURing(env.SMPPDU_RING_CAPACITY),
180 mtu(
number(Defaults::MIN_SMP_MTU))
182 if( !validateConnected() ) {
183 ERR_PRINT(
"SMPHandler.ctor: L2CAP could not connect");
184 is_connected =
false;
189 smp_reader_service.start();
191 DBG_PRINT(
"SMPHandler::ctor: Started: SMPHandler[%s], l2cap[%s]: %s",
192 getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
195 uint16_t mtu_ =
number(Defaults::MIN_SMP_MTU);
216 if( !is_connected.compare_exchange_strong(expConn,
false) ) {
218 const bool smp_service_stopped = smp_reader_service.join();
220 DBG_PRINT(
"SMPHandler::disconnect: Not connected: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s], stopped %d: %s",
221 disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(),
222 smp_service_stopped, deviceString.c_str());
228 const bool smp_service_stop_res = smp_reader_service.stop();
233 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
234 DBG_PRINT(
"SMPHandler::disconnect: Start: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
235 disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
238 DBG_PRINT(
"SMPHandler::disconnect: End: stopped %d, %s", smp_service_stop_res, deviceString.c_str());
240 if( disconnectDevice ) {
241 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
242 if(
nullptr != device ) {
248 device->disconnect(reason);
254void SMPHandler::send(
const SMPPDUMsg & msg) {
255 if( !validateConnected() ) {
267 ERR_PRINT(
"l2cap write: Error res %d (%s); %s; %s -> disconnect: %s",
269 msg.
toString().c_str(), deviceString.c_str());
274 if(
static_cast<size_t>(len) != msg.
pdu.
size() ) {
275 ERR_PRINT(
"l2cap write: Error: Message size has %d != exp %zu: %s -> disconnect: %s",
283 WORDY_PRINT(
"SMPHandler::reader: l2cap read: IRQed res %d (%s); %s",
292 std::unique_ptr<const SMPPDUMsg> res;
293 if( !smpPDURing.getBlocking(res, timeout) ||
nullptr == res ) {
295 IRQ_PRINT(
"SMPHandler::sendWithReply: nullptr result (timeout %d): req %s to %s", timeout, msg.
toString().c_str(), deviceString.c_str());
318void SMPHandler::clearAllCallbacks() noexcept {
319 smpSecurityReqCallbackList.
clear();
static SMPHandler::SMPSecurityReqCallbackList::equal_comparator _changedSMPSecurityReqCallbackEqComp
SMPSecurityReqCallback handling.
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect the LE or BREDR peer's GATT and HCI connection.
static std::string getRWExitCodeString(const RWExitCode ec) noexcept
std::string getStateString() const noexcept override
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length) noexcept
Generic write, locking mutex_write().
static constexpr int number(const Defaults d) noexcept
bool hasIOError() const noexcept
@ READ_TIMEOUT
READ_TIMEOUT.
@ POLL_TIMEOUT
POLL_TIMEOUT.
@ INTERRUPTED
INTERRUPTED.
bool is_open() const noexcept
static SMPEnv & get() noexcept
void addSMPSecurityReqCallback(const SMPSecurityReqCallback &l)
static bool IS_SUPPORTED_BY_OS
Linux/BlueZ prohibits access to the existing SMP implementation via L2CAP (socket).
SMPHandler(const std::shared_ptr< BTDevice > &device) noexcept
size_type removeSMPSecurityReqCallback(const SMPSecurityReqCallback &l)
std::shared_ptr< BTDevice > getDeviceChecked() const
~SMPHandler() noexcept
Destructor closing this instance including L2CAP channel, see disconnect().
bool establishSecurity(const BTSecurityLevel sec_level)
If sec_level > BTSecurityLevel::UNSET, change security level per L2CAP connection.
std::string getStateString() const noexcept
bool disconnect(const bool disconnectDevice, const bool ioErrorCause) noexcept
Disconnect this GATTHandler and optionally the associated device.
Handles the Security Manager Protocol (SMP) using Protocol Data Unit (PDU) encoded messages over L2CA...
jau::POctets pdu
actual received PDU
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.
virtual std::string toString() const noexcept
Opcode
SMP Command Codes Vol 3, Part H (SM): 3.3.
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
constexpr uint8_t const * get_ptr() const noexcept
constexpr_atomic void push_back(const value_type &x)
Like std::vector::push_back(), copy.
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
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.
Main jau environment class, supporting environment variable access and fetching elapsed time using it...
Service runner, a reusable dedicated thread performing custom user services.
bool shall_stop2(int dummy) noexcept
Helper function to easy FunctionDef usage w/o creating a lambda alike capture with same semantics as ...
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
@ little
Identifier for little endian, equivalent to endian::little.
std::string to_string(const alphabet &v) noexcept
constexpr const bool SMP_SUPPORTED_BY_OS
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
std::shared_ptr< BTDevice > BTDeviceRef
BTSecurityLevel
Bluetooth Security Level.
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
constexpr uint8_t number(const DiscoveryPolicy rhs) noexcept
@ REMOTE_USER_TERMINATED_CONNECTION
@ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
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...
@ timeout
Input or output operation failed due to timeout.
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.