diff options
author | Sven Gothel <[email protected]> | 2022-01-12 05:43:28 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-01-12 05:43:28 +0100 |
commit | fd4c43ff56b5ca83b6e225a108f1eb751f5c6203 (patch) | |
tree | d7790181481791fc14d530a8bccc28ca6adbecc0 /src | |
parent | 83c7e0459e381e2cd412959ff5917bfce2b80ecf (diff) |
Refine L2CAPComm and add L2CAPServer (listen for connecting remote devices)
Diffstat (limited to 'src')
-rw-r--r-- | src/direct_bt/L2CAPComm.cpp | 324 |
1 files changed, 263 insertions, 61 deletions
diff --git a/src/direct_bt/L2CAPComm.cpp b/src/direct_bt/L2CAPComm.cpp index 0e2fce90..2a060198 100644 --- a/src/direct_bt/L2CAPComm.cpp +++ b/src/direct_bt/L2CAPComm.cpp @@ -61,7 +61,7 @@ L2CAPEnv::L2CAPEnv() noexcept { } -int L2CAPComm::l2cap_open_dev(const BDAddressAndType & adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) { +int L2CAPComm::l2cap_open_dev(const BDAddressAndType & adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept { sockaddr_l2 a; int fd, err; @@ -96,7 +96,7 @@ failed: return -1; } -int L2CAPComm::l2cap_close_dev(int dd) +int L2CAPComm::l2cap_close_dev(int dd) noexcept { return ::close(dd); } @@ -106,15 +106,24 @@ int L2CAPComm::l2cap_close_dev(int dd) // ************************************************* // ************************************************* -L2CAPComm::L2CAPComm(const BDAddressAndType& adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) +L2CAPComm::L2CAPComm(const BDAddressAndType& adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) noexcept : env(L2CAPEnv::get()), - adapterAddressAndType(adapterAddressAndType_), + localAddressAndType(adapterAddressAndType_), psm(psm_), cid(cid_), - deviceAddressAndType(BDAddressAndType::ANY_BREDR_DEVICE), - socket_descriptor(-1), + remoteAddressAndType(BDAddressAndType::ANY_BREDR_DEVICE), + client_socket(-1), is_open(false), has_ioerror(false), interrupt_flag(false), tid_connect(0), tid_read(0) { } +L2CAPComm::L2CAPComm(const BDAddressAndType& adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_, + const BDAddressAndType& remoteAddressAndType_, int client_socket_) noexcept +: env(L2CAPEnv::get()), + localAddressAndType(adapterAddressAndType_), + psm(psm_), cid(cid_), + remoteAddressAndType(remoteAddressAndType_), + client_socket(client_socket_), + is_open(0 <= client_socket_), has_ioerror(false), interrupt_flag(false), tid_connect(0), tid_read(0) +{ } /** * Setting BT_SECURITY within open() after bind() and before connect() @@ -127,13 +136,13 @@ L2CAPComm::L2CAPComm(const BDAddressAndType& adapterAddressAndType_, const L2CAP */ #define SET_BT_SECURITY_POST_CONNECT 1 -bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) { +bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) noexcept { bool expOpen = false; // C++11, exp as value since C++20 if( !is_open.compare_exchange_strong(expOpen, true) ) { DBG_PRINT("L2CAPComm::open(%s, %s): Already open: dd %d, %s, psm %s, cid %s; %s", device.getAddressAndType().toString().c_str(), to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return false; @@ -157,7 +166,7 @@ bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) { * - l2cap_connect with destination address * -- connect(fd, ..) */ - deviceAddressAndType = device.getAddressAndType(); + remoteAddressAndType = device.getAddressAndType(); /** BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ */ sockaddr_l2 req; @@ -165,13 +174,13 @@ bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) { int to_retry_count=0; // ETIMEDOUT retry count DBG_PRINT("L2CAPComm::open: Start Connect: dd %d, %s, psm %s, cid %s, sec_level %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(), getStateString().c_str()); - socket_descriptor = l2cap_open_dev(adapterAddressAndType, psm, cid); + client_socket = l2cap_open_dev(localAddressAndType, psm, cid); - if( 0 > socket_descriptor ) { + if( 0 > client_socket ) { goto failure; // open failed } @@ -191,17 +200,17 @@ bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) { bzero((void *)&req, sizeof(req)); req.l2_family = AF_BLUETOOTH; req.l2_psm = jau::cpu_to_le(direct_bt::number(psm)); - req.l2_bdaddr = jau::cpu_to_le(deviceAddressAndType.address); + req.l2_bdaddr = jau::cpu_to_le(remoteAddressAndType.address); req.l2_cid = jau::cpu_to_le(direct_bt::number(cid)); - req.l2_bdaddr_type = ::number(deviceAddressAndType.type); + req.l2_bdaddr_type = ::number(remoteAddressAndType.type); while( !interrupt_flag ) { // blocking - res = ::connect(socket_descriptor, (struct sockaddr*)&req, sizeof(req)); + res = ::connect(client_socket, (struct sockaddr*)&req, sizeof(req)); - DBG_PRINT("L2CAPComm::open: Connect Result: %d, errno 0%X %s, %s, psm %s, cid %s", + DBG_PRINT("L2CAPComm::open: Connect Result: %d, errno 0x%X %s, %s, psm %s, cid %s", res, errno, strerror(errno), - deviceAddressAndType.toString().c_str(), + remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str()); if( !res ) @@ -213,26 +222,28 @@ bool L2CAPComm::open(const BTDevice& device, const BTSecurityLevel sec_level) { if( to_retry_count < number(Defaults::L2CAP_CONNECT_MAX_RETRY) ) { WORDY_PRINT("L2CAPComm::open: Connect timeout, retry %d: dd %d, %s, psm %s, cid %s, sec_level %s; %s", to_retry_count, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(), getStateString().c_str()); continue; } else { ERR_PRINT("L2CAPComm::open: Connect timeout, retried %d: dd %d, %s, psm %s, cid %s, sec_level %s; %s", to_retry_count, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(), getStateString().c_str()); goto failure; // exit } - } else { + } else if( !interrupt_flag ) { // EALREADY == errno || ENETUNREACH == errno || EHOSTUNREACH == errno || .. ERR_PRINT("L2CAPComm::open: Connect failed: dd %d, %s, psm %s, cid %s, sec_level %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(), getStateString().c_str()); goto failure; // exit + } else { + goto failure; // exit on interrupt } } // success @@ -261,7 +272,7 @@ bool L2CAPComm::close() noexcept { bool expOpen = true; // C++11, exp as value since C++20 if( !is_open.compare_exchange_strong(expOpen, false) ) { DBG_PRINT("L2CAPComm::close: Not connected: dd %d, %s, psm %s, cid %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); has_ioerror = false; // always clear last ioerror flag (should be redundant) @@ -271,7 +282,7 @@ bool L2CAPComm::close() noexcept { has_ioerror = false; DBG_PRINT("L2CAPComm::close: Start: dd %d, %s, psm %s, cid %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); PERF_TS_T0(); @@ -290,7 +301,7 @@ bool L2CAPComm::close() noexcept { if( 0 != ( kerr = pthread_kill(_tid_read, SIGALRM) ) ) { ERR_PRINT("L2CAPComm::close: pthread_kill read %p FAILED: %d; dd %d, %s, psm %s, cid %s; %s", (void*)_tid_read, kerr, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } @@ -302,43 +313,51 @@ bool L2CAPComm::close() noexcept { if( 0 != ( kerr = pthread_kill(_tid_connect, SIGALRM) ) ) { ERR_PRINT("L2CAPComm::close: Start: pthread_kill connect %p FAILED: %d; dd %d, %s, psm %s, cid %s; %s", (void*)_tid_connect, kerr, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } } } - l2cap_close_dev(socket_descriptor); - socket_descriptor = -1; + l2cap_close_dev(client_socket); + client_socket = -1; interrupt_flag = false; PERF_TS_TD("L2CAPComm::close"); DBG_PRINT("L2CAPComm::close: End: dd %d, %s, psm %s, cid %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return true; } -bool L2CAPComm::setBTSecurityLevel(const BTSecurityLevel sec_level) { +bool L2CAPComm::setBTSecurityLevel(const BTSecurityLevel sec_level) noexcept { if( !is_open ) { DBG_PRINT("L2CAPComm::setBTSecurityLevel(%s): Not connected: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return false; } const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor - - return setBTSecurityLevelImpl(sec_level); + if( BTSecurityLevel::UNSET < sec_level ) { + if( setBTSecurityLevelImpl(sec_level) ) { + return true; + } else { + close(); + return false; + } + } else { + return true; + } } -bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) { +bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) noexcept { if( BTSecurityLevel::NONE > sec_level ) { DBG_PRINT("L2CAPComm::setBTSecurityLevel: sec_level %s not set: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return false; @@ -352,18 +371,18 @@ bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) { if( old_sec_level != sec_level ) { bzero(&bt_sec, sizeof(bt_sec)); bt_sec.level = direct_bt::number(sec_level); - result = setsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec)); + result = setsockopt(client_socket, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec)); if ( 0 == result ) { DBG_PRINT("L2CAPComm::setBTSecurityLevel: Success: sec_level %s -> %s: dd %d, %s, psm %s, cid %s; %s", to_string(old_sec_level).c_str(), to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return true; } else { ERR_PRINT("L2CAPComm::setBTSecurityLevel: Failed: sec_level %s -> %s: dd %d, %s, psm %s, cid %s; %s", to_string(old_sec_level).c_str(), to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return false; @@ -371,7 +390,7 @@ bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) { } else { DBG_PRINT("L2CAPComm::setBTSecurityLevel: Unchanged: sec_level %s -> %s: dd %d, %s, psm %s, cid %s; %s", to_string(old_sec_level).c_str(), to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return true; @@ -379,17 +398,17 @@ bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level) { #else DBG_PRINT("L2CAPComm::setBTSecurityLevel: Not implemented: sec_level %s: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return false; #endif } -BTSecurityLevel L2CAPComm::getBTSecurityLevel() { +BTSecurityLevel L2CAPComm::getBTSecurityLevel() noexcept { if( !is_open ) { DBG_PRINT("L2CAPComm::getBTSecurityLevel: Not connected: dd %d, %s, psm %s, cid %s; %s", - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); return BTSecurityLevel::UNSET; @@ -399,7 +418,7 @@ BTSecurityLevel L2CAPComm::getBTSecurityLevel() { return getBTSecurityLevelImpl(); } -BTSecurityLevel L2CAPComm::getBTSecurityLevelImpl() { +BTSecurityLevel L2CAPComm::getBTSecurityLevelImpl() noexcept { BTSecurityLevel sec_level = BTSecurityLevel::UNSET; #if USE_LINUX_BT_SECURITY struct bt_security bt_sec; @@ -407,33 +426,33 @@ BTSecurityLevel L2CAPComm::getBTSecurityLevelImpl() { int result; bzero(&bt_sec, sizeof(bt_sec)); - result = getsockopt(socket_descriptor, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, &optlen); + result = getsockopt(client_socket, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, &optlen); if ( 0 == result ) { if( optlen == sizeof(bt_sec) ) { sec_level = static_cast<BTSecurityLevel>(bt_sec.level); DBG_PRINT("L2CAPComm::getBTSecurityLevel: Success: sec_level %s: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } else { ERR_PRINT("L2CAPComm::getBTSecurityLevel: Failed: sec_level %s, size %zd returned != %zd bt_sec: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), optlen, sizeof(bt_sec), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } } else { ERR_PRINT("L2CAPComm::getBTSecurityLevel: Failed: sec_level %s, result %d: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), result, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } #else DBG_PRINT("L2CAPComm::getBTSecurityLevel: Not implemented: sec_level %s: dd %d, %s, psm %s, cid %s; %s", to_string(sec_level).c_str(), - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); #endif @@ -463,7 +482,7 @@ std::string L2CAPComm::getRWExitCodeString(const RWExitCode ec) noexcept { return "Unknown ExitCode"; } -jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) { +jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) noexcept { const int32_t timeoutMS = env.L2CAP_READER_POLL_TIMEOUT; jau::snsize_t len = 0; jau::snsize_t err_res = 0; @@ -476,7 +495,7 @@ jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) { err_res = number(RWExitCode::INTERRUPTED); goto errout; } - if( 0 > socket_descriptor ) { + if( 0 > client_socket ) { err_res = number(RWExitCode::INVALID_SOCKET_DD); goto errout; } @@ -490,7 +509,7 @@ jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) { struct pollfd p; int n; - p.fd = socket_descriptor; p.events = POLLIN; + p.fd = client_socket; p.events = POLLIN; while ( is_open && !interrupt_flag && ( n = poll( &p, 1, timeoutMS ) ) < 0 ) { if( !is_open ) { err_res = number(RWExitCode::NOT_OPEN); @@ -514,7 +533,7 @@ jau::snsize_t L2CAPComm::read(uint8_t* buffer, const jau::nsize_t capacity) { } } - while ( is_open && !interrupt_flag && ( len = ::read(socket_descriptor, buffer, capacity) ) < 0 ) { + while ( is_open && !interrupt_flag && ( len = ::read(client_socket, buffer, capacity) ) < 0 ) { if( !is_open ) { err_res = number(RWExitCode::NOT_OPEN); goto errout; @@ -541,7 +560,7 @@ errout: // closed or intentionally interrupted WORDY_PRINT("L2CAPComm::read: IRQed res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } else { @@ -551,7 +570,7 @@ errout: if( err_res != number(RWExitCode::POLL_TIMEOUT) ) { // expected POLL_TIMEOUT if idle DBG_PRINT("L2CAPComm::read: Timeout res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } @@ -562,13 +581,13 @@ errout: if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) { ABORT("L2CAPComm::read: Error res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } else { IRQ_PRINT("L2CAPComm::read: Error res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } @@ -577,7 +596,7 @@ errout: return err_res; } -jau::snsize_t L2CAPComm::write(const uint8_t * buffer, const jau::nsize_t length) { +jau::snsize_t L2CAPComm::write(const uint8_t * buffer, const jau::nsize_t length) noexcept { const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor jau::snsize_t len = 0; jau::snsize_t err_res = 0; @@ -590,7 +609,7 @@ jau::snsize_t L2CAPComm::write(const uint8_t * buffer, const jau::nsize_t length err_res = number(RWExitCode::INTERRUPTED); goto errout; } - if( 0 > socket_descriptor ) { + if( 0 > client_socket ) { err_res = number(RWExitCode::INVALID_SOCKET_DD); goto errout; } @@ -598,7 +617,7 @@ jau::snsize_t L2CAPComm::write(const uint8_t * buffer, const jau::nsize_t length goto done; } - while ( is_open && !interrupt_flag && ( len = ::write(socket_descriptor, buffer, length) ) < 0 ) { + while ( is_open && !interrupt_flag && ( len = ::write(client_socket, buffer, length) ) < 0 ) { if( !is_open ) { err_res = number(RWExitCode::NOT_OPEN); goto errout; @@ -623,7 +642,7 @@ errout: // closed or intentionally interrupted WORDY_PRINT("L2CAPComm::write: IRQed res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } else { @@ -633,13 +652,13 @@ errout: if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) { ABORT("L2CAPComm::write: Error res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } else { IRQ_PRINT("L2CAPComm::write: Error res %d (%s), len %d; dd %d, %s, psm %s, cid %s; %s", err_res, getRWExitCodeString(err_res).c_str(), len, - socket_descriptor.load(), deviceAddressAndType.toString().c_str(), + client_socket.load(), remoteAddressAndType.toString().c_str(), to_string(psm).c_str(), to_string(cid).c_str(), getStateString().c_str()); } @@ -647,3 +666,186 @@ errout: return err_res; } +std::string L2CAPComm::toString() { + return "L2CAPComm[dd "+std::to_string(client_socket.load())+ + ", psm "+to_string(psm)+ + ", cid "+to_string(cid)+ + ", local "+localAddressAndType.toString()+ + ", remote "+remoteAddressAndType.toString()+ + ", "+getStateString()+"]"; +} + +// ************************************************* +// ************************************************* +// ************************************************* + +L2CAPServer::L2CAPServer(const BDAddressAndType& localAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) noexcept +: localAddressAndType(localAddressAndType_), + psm(psm_), cid(cid_), + server_socket(-1), + is_open(false), interrupt_flag(false), tid_accept(0) +{ } + +bool L2CAPServer::open() noexcept { + bool expOpen = false; // C++11, exp as value since C++20 + if( !is_open.compare_exchange_strong(expOpen, true) ) { + DBG_PRINT("L2CAPServer::open: Already open: dd %d, psm %s, cid %s, local %s", + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + return false; + } + const std::lock_guard<std::recursive_mutex> lock(mtx_open); // RAII-style acquire and relinquish via destructor + int res; + + DBG_PRINT("L2CAPServer::open: Start: dd %d, psm %s, cid %s, local %s", + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + + server_socket = L2CAPComm::l2cap_open_dev(localAddressAndType, psm, cid); + + if( 0 > server_socket ) { + goto failure; // open failed + } + +#if !SET_BT_SECURITY_POST_CONNECT + #if USE_LINUX_BT_SECURITY + if( BTSecurityLevel::UNSET < sec_level ) { + if( !setBTSecurityLevelImpl(sec_level) ) { + goto failure; // sec_level failed + } + } + #endif +#endif + + res = ::listen(server_socket, 10); + + DBG_PRINT("L2CAPServer::open: End: res %d, dd %d, psm %s, cid %s, local %s", + res, server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + + if( res < 0 ) { + goto failure; + } + + return true; + +failure: + const int err = errno; + close(); + errno = err; + return false; +} + +bool L2CAPServer::close() noexcept { + bool expOpen = true; // C++11, exp as value since C++20 + if( !is_open.compare_exchange_strong(expOpen, false) ) { + DBG_PRINT("L2CAPServer::close: Not connected: dd %d, psm %s, cid %s, local %s", + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + return true; + } + const std::lock_guard<std::recursive_mutex> lock(mtx_open); // RAII-style acquire and relinquish via destructor + + DBG_PRINT("L2CAPServer::close: Start: dd %d, psm %s, cid %s, local %s", + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + PERF_TS_T0(); + + interrupt_flag = true; + { + pthread_t tid_self = pthread_self(); + pthread_t _tid_accept = tid_accept; + tid_accept = 0; + + // interrupt connect(..) and , avoiding prolonged hang + interrupt_flag = true; + if( 0 != _tid_accept && tid_self != _tid_accept ) { + int kerr; + if( 0 != ( kerr = pthread_kill(_tid_accept, SIGALRM) ) ) { + ERR_PRINT("L2CAPServer::close: Start: pthread_kill connect %p FAILED: %d; dd %d, psm %s, cid %s, local %s", + (void*)_tid_accept, kerr, + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + } + } + } + + L2CAPComm::l2cap_close_dev(server_socket); + server_socket = -1; + interrupt_flag = false; + PERF_TS_TD("L2CAPServer::close"); + DBG_PRINT("L2CAPServer::close: End: dd %d, psm %s, cid %s, local %s", + server_socket.load(), to_string(psm).c_str(), to_string(cid).c_str(), + localAddressAndType.toString().c_str()); + return true; +} + +std::unique_ptr<L2CAPComm> L2CAPServer::accept() noexcept { + sockaddr_l2 peer; + int to_retry_count=0; // ETIMEDOUT retry count + + tid_accept = pthread_self(); // temporary safe tid to allow interruption + + while( !interrupt_flag ) { + // blocking + bzero((void *)&peer, sizeof(peer)); + socklen_t addrlen = sizeof(peer); // on return it will contain the actual size of the peer address + int client_socket = ::accept(server_socket, (struct sockaddr*)&peer, &addrlen); + + BDAddressAndType remoteAddressAndType(jau::le_to_cpu(peer.l2_bdaddr), static_cast<BDAddressType>(peer.l2_bdaddr_type)); + L2CAP_PSM c_psm = static_cast<L2CAP_PSM>(jau::le_to_cpu(peer.l2_psm)); + L2CAP_CID c_cid = static_cast<L2CAP_CID>(jau::le_to_cpu(peer.l2_cid)); + + if( 0 <= client_socket ) + { + DBG_PRINT("L2CAPServer::accept: Success: dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s", + server_socket.load(), client_socket, errno, strerror(errno), + to_string(psm).c_str(), to_string(c_psm).c_str(), + to_string(cid).c_str(), to_string(c_cid).c_str(), + localAddressAndType.toString().c_str(), + remoteAddressAndType.toString().c_str()); + // success + tid_accept = 0; + return std::make_unique<L2CAPComm>(localAddressAndType, c_psm, c_cid, remoteAddressAndType, client_socket); + } else if( ETIMEDOUT == errno ) { + to_retry_count++; + if( to_retry_count < L2CAPComm::number(L2CAPComm::Defaults::L2CAP_CONNECT_MAX_RETRY) ) { + WORDY_PRINT("L2CAPServer::accept: Timeout # %d (retry): dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s", + to_retry_count, server_socket.load(), client_socket, errno, strerror(errno), + to_string(psm).c_str(), to_string(c_psm).c_str(), + to_string(cid).c_str(), to_string(c_cid).c_str(), + localAddressAndType.toString().c_str(), + remoteAddressAndType.toString().c_str()); + continue; + } else { + ERR_PRINT("L2CAPServer::accept: Timeout # %d (done): dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s", + to_retry_count, server_socket.load(), client_socket, errno, strerror(errno), + to_string(psm).c_str(), to_string(c_psm).c_str(), + to_string(cid).c_str(), to_string(c_cid).c_str(), + localAddressAndType.toString().c_str(), + remoteAddressAndType.toString().c_str()); + break; // exit + } + + } else if( !interrupt_flag ) { + // EALREADY == errno || ENETUNREACH == errno || EHOSTUNREACH == errno || .. + ERR_PRINT("L2CAPServer::accept: Failed: dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s", + server_socket.load(), client_socket, errno, strerror(errno), + to_string(psm).c_str(), to_string(c_psm).c_str(), + to_string(cid).c_str(), to_string(c_cid).c_str(), + localAddressAndType.toString().c_str(), + remoteAddressAndType.toString().c_str()); + break; // exit + } + } + // failure + tid_accept = 0; + return nullptr; +} + +std::string L2CAPServer::toString() { + return "L2CAPServer[dd "+std::to_string(server_socket.load())+ + ", psm "+to_string(psm)+ + ", cid "+to_string(cid)+ + ", local "+localAddressAndType.toString()+"]"; +} |