35 #include <sys/socket.h>
44#include "L2CAPIoctl.hpp"
60 void close_impl()
noexcept {}
67 void close() noexcept
override { close_impl(); }
115 void close_impl()
noexcept {
117 if(
nullptr == device ) {
123 l->disconnected(device);
124 } catch (std::exception &e) {
125 ERR_PRINT(
"%d/%zd: %s: Caught exception %s",
126 i+1, gattServerData->listener().size(),
132 writeDataQueue.
clear();
133 writeDataQueueHandles.
clear();
138 : gh(gh_), gattServerData(std::move(gsd)) {}
142 void close() noexcept
override { close_impl(); }
145 bool hasServerHandle(
const uint16_t handle)
noexcept {
147 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
149 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
150 if( handle == c->getValueHandle() ) {
154 if( handle == d->getHandle() ) {
165 DBGattCharRef findServerGattCharByValueHandle(
const uint16_t char_value_handle)
noexcept {
166 return gattServerData->findGattCharByValueHandle(char_value_handle);
171 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
173 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
174 if( handle == c->getValueHandle() ) {
175 if( c->getValue().size() < value_offset) {
176 return AttErrorRsp::ErrorCode::INVALID_OFFSET;
178 if( c->hasVariableLength() ) {
179 if( c->getValue().capacity() < value_offset + value.size() ) {
180 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
183 if( c->getValue().size() < value_offset + value.size() ) {
184 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
192 allowed = l->writeCharValue(device, s, c, value, value_offset) && allowed;
193 } catch (std::exception &e) {
194 ERR_PRINT(
"GATT-REQ: WRITE: (%s) %d/%zd: %s of %s: Caught exception %s",
195 c->toString().c_str(), i+1, gattServerData->listener().size(),
196 device->toString().c_str(), e.what());
201 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
204 if( c->hasVariableLength() ) {
205 if( c->getValue().size() != value_offset + value.size() ) {
206 c->getValue().resize( value_offset + value.size() );
209 c->getValue().put_octets_nc(value_offset, value);
210 return AttErrorRsp::ErrorCode::NO_ERROR;
213 if( handle == d->getHandle() ) {
214 if( d->getValue().size() < value_offset) {
215 return AttErrorRsp::ErrorCode::INVALID_OFFSET;
217 if( d->hasVariableLength() ) {
218 if( d->getValue().capacity() < value_offset + value.size() ) {
219 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
222 if( d->getValue().size() < value_offset + value.size() ) {
223 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
226 if( d->isUserDescription() ) {
227 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
229 const bool isCCCD = d->isClientCharConfig();
235 allowed = l->writeDescValue(device, s, c, d, value, value_offset) && allowed;
236 } catch (std::exception &e) {
237 ERR_PRINT(
"GATT-REQ: WRITE: (%s) %d/%zd: %s of %s: Caught exception %s",
238 d->toString().c_str(), i+1, gattServerData->listener().size(),
239 device->toString().c_str(), e.what());
244 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
247 if( d->hasVariableLength() ) {
248 if( d->getValue().size() != value_offset + value.size() ) {
249 d->getValue().resize( value_offset + value.size() );
253 if( value.size() == 0 ) {
255 return AttErrorRsp::ErrorCode::NO_ERROR;
257 const uint8_t old_v = d->getValue().get_uint8_nc(0);
258 const bool oldEnableNotification = old_v & 0b001;
259 const bool oldEnableIndication = old_v & 0b010;
261 const uint8_t req_v = value.get_uint8_nc(0);
262 const bool reqEnableNotification = req_v & 0b001;
263 const bool reqEnableIndication = req_v & 0b010;
266 const bool enableNotification = reqEnableNotification && hasNotification;
267 const bool enableIndication = reqEnableIndication && hasIndication;
269 if( oldEnableNotification == enableNotification &&
270 oldEnableIndication == enableIndication ) {
272 return AttErrorRsp::ErrorCode::NO_ERROR;
274 const uint16_t new_v = enableNotification | ( enableIndication << 1 );
275 d->getValue().put_uint8_nc(0, new_v);
280 l->clientCharConfigChanged(device, s, c, d, enableNotification, enableIndication);
281 } catch (std::exception &e) {
282 ERR_PRINT(
"GATT-REQ: WRITE CCCD: (%s) %d/%zd: %s of %s: Caught exception %s",
283 d->toString().c_str(), i+1, gattServerData->listener().size(),
284 device->toString().c_str(), e.what());
291 d->getValue().put_octets_nc(value_offset, value);
293 return AttErrorRsp::ErrorCode::NO_ERROR;
300 return AttErrorRsp::ErrorCode::INVALID_HANDLE;
303 void signalWriteDone(
BTDeviceRef device,
const uint16_t handle)
noexcept {
305 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
307 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
308 if( handle == c->getValueHandle() ) {
313 l->writeCharValueDone(device, s, c);
314 } catch (std::exception &e) {
315 ERR_PRINT(
"GATT-REQ: WRITE-Done: (%s) %d/%zd: %s of %s: Caught exception %s",
316 c->toString().c_str(), i+1, gattServerData->listener().size(),
317 device->toString().c_str(), e.what());
325 if( handle == d->getHandle() ) {
326 if( d->isUserDescription() ) {
329 const bool isCCCD = d->isClientCharConfig();
334 l->writeDescValueDone(device, s, c, d);
335 } catch (std::exception &e) {
336 ERR_PRINT(
"GATT-REQ: WRITE-Done: (%s) %d/%zd: %s of %s: Caught exception %s",
337 d->toString().c_str(), i+1, gattServerData->listener().size(),
338 device->toString().c_str(), e.what());
358 const uint16_t clientMTU = pdu->getMTUSize();
359 gh.setUsedMTU(
std::min(gh.getServerMTU(), clientMTU) );
360 const AttExchangeMTU rsp(AttPDUMsg::ReqRespType::RESPONSE, gh.getUsedMTU());
361 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: MTU recv: %u, %s -> %u %s from %s",
362 clientMTU, pdu->toString().c_str(),
363 gh.getUsedMTU(), rsp.
toString().c_str(), gh.toString().c_str());
364 if(
nullptr != gattServerData ) {
366 if(
nullptr != device ) {
370 l->mtuChanged(device, gh.getUsedMTU());
371 } catch (std::exception &e) {
372 ERR_PRINT(
"%d/%zd: %s: Caught exception %s",
373 i+1, gattServerData->listener().size(),
374 gh.toString().c_str(), e.what());
398 if(
nullptr == device ) {
399 AttErrorRsp err(AttErrorRsp::ErrorCode::UNLIKELY_ERROR, pdu->getOpcode(), 0);
400 ERR_PRINT(
"GATT-Req: WRITE.0, null device: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
404 if( AttPDUMsg::Opcode::PREPARE_WRITE_REQ == pdu->getOpcode() ) {
406 if( !hasServerHandle( req->
getHandle() ) ) {
411 const uint16_t handle = req->
getHandle();
413 writeDataQueue.push_back(rsp);
414 if( writeDataQueueHandles.cend() ==
jau::find_if(writeDataQueueHandles.cbegin(), writeDataQueueHandles.cend(),
415 [&](
const uint16_t it)->bool { return handle == it; }) )
418 writeDataQueueHandles.push_back(handle);
420 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: WRITE.11: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
422 }
else if( AttPDUMsg::Opcode::EXECUTE_WRITE_REQ == pdu->getOpcode() ) {
426 for(
auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
427 for(
auto iter_prep_write = writeDataQueue.cbegin(); iter_prep_write < writeDataQueue.cend(); ++iter_prep_write ) {
430 if( handle == *iter_handle ) {
435 if( AttErrorRsp::ErrorCode::NO_ERROR != res ) {
436 writeDataQueue.clear();
437 writeDataQueueHandles.clear();
439 WARN_PRINT(
"GATT-Req: WRITE.12: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
445 for(
auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
446 signalWriteDone(device, *iter_handle);
449 writeDataQueue.clear();
450 writeDataQueueHandles.clear();
452 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: WRITE.13: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
459 if( AttPDUMsg::Opcode::WRITE_REQ == pdu->getOpcode() ) {
464 }
else if( AttPDUMsg::Opcode::WRITE_CMD == pdu->getOpcode() ) {
471 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
472 WARN_PRINT(
"GATT-Req: WRITE.20: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
477 if( AttErrorRsp::ErrorCode::NO_ERROR != res ) {
479 WARN_PRINT(
"GATT-Req: WRITE.21: %s -> %s (sent %d) from %s",
480 pdu->toString().c_str(), err.
toString().c_str(), (
int)withResp, gh.toString().c_str());
488 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: WRITE.22: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
489 if( !gh.send(rsp) ) {
493 signalWriteDone(device, handle);
502 if(
nullptr == device ) {
503 AttErrorRsp err(AttErrorRsp::ErrorCode::UNLIKELY_ERROR, pdu->getOpcode(), 0);
504 ERR_PRINT(
"GATT-Req: READ, null device: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
508 uint16_t value_offset = 0;
510 if( AttPDUMsg::Opcode::READ_REQ == pdu->getOpcode() ) {
514 }
else if( AttPDUMsg::Opcode::READ_BLOB_REQ == pdu->getOpcode() ) {
528 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
529 WARN_PRINT(
"GATT-Req: READ: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
533 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
534 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
541 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
547 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
548 if( handle == c->getValueHandle() ) {
550#if SEND_ATTRIBUTE_NOT_LONG
551 if( c->getValue().size() <= rspMaxSize ) {
552 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_LONG, pdu->getOpcode(), handle);
553 COND_PRINT(env.DEBUG_DATA,
"GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), toString().c_str());
558 if( value_offset > c->getValue().size() ) {
559 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_OFFSET, pdu->getOpcode(), handle);
560 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
569 allowed = l->readCharValue(device, s, c) && allowed;
570 } catch (std::exception &e) {
571 ERR_PRINT(
"GATT-REQ: READ: (%s) %d/%zd: %s of %s: Caught exception %s",
572 c->toString().c_str(), i+1, gattServerData->listener().size(),
573 device->toString().c_str(), e.what());
578 AttErrorRsp err(AttErrorRsp::ErrorCode::NO_READ_PERM, pdu->getOpcode(), handle);
579 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.2: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
583 AttReadNRsp rsp(isBlobReq, c->getValue(), value_offset);
587 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.3: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
591 if( handle == d->getHandle() ) {
593#if SEND_ATTRIBUTE_NOT_LONG
594 if( isBlobReq && d->getValue().size() <= rspMaxSize ) {
595 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_LONG, pdu->getOpcode(), handle);
596 COND_PRINT(env.DEBUG_DATA,
"GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), toString().c_str());
601 if( value_offset > c->getValue().size() ) {
602 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_OFFSET, pdu->getOpcode(), handle);
603 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
612 allowed = l->readDescValue(device, s, c, d) && allowed;
613 } catch (std::exception &e) {
614 ERR_PRINT(
"GATT-REQ: READ: (%s) %d/%zd: %s of %s: Caught exception %s",
615 d->toString().c_str(), i+1, gattServerData->listener().size(),
616 device->toString().c_str(), e.what());
621 AttErrorRsp err(AttErrorRsp::ErrorCode::NO_READ_PERM, pdu->getOpcode(), handle);
622 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.4: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
626 AttReadNRsp rsp(isBlobReq, d->getValue(), value_offset);
630 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.5: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
638 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), handle);
639 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: READ.6: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
647 if( 0 == pdu->getStartHandle() ) {
648 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
649 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: INFO.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
652 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
653 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
654 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: INFO.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
657 const uint16_t end_handle = pdu->getEndHandle();
658 const uint16_t start_handle = pdu->getStartHandle();
660 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
669 if( start_handle <= d->getHandle() && d->getHandle() <= end_handle ) {
670 const jau::nsize_t size = 2 + d->getType()->getTypeSizeInt();
671 if( 0 == rspElemSize ) {
676 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
679 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: INFO.2: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
692 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: INFO.3: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
695 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), start_handle);
696 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: INFO.4: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
704 if( 0 == pdu->getStartHandle() ) {
705 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
706 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPEVALUE.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
709 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
710 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
711 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPEVALUE.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
716 const uint16_t end_handle = pdu->getEndHandle();
717 const uint16_t start_handle = pdu->getStartHandle();
719 std::unique_ptr<const jau::uuid_t> att_value = pdu->getAttValue();
721 uint16_t req_group_type;
722 if( att_type.
equivalent( uuid_prim_service ) ) {
724 }
else if( att_type.
equivalent( uuid_secd_service ) ) {
740 if( start_handle <= s->getHandle() && s->getHandle() <= end_handle ) {
744 s->getType()->equivalent(*att_value) )
749 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPEVALUE.4: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
756 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPEVALUE.5: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
762 ERR_PRINT(
"invalid att uuid: Unknown exception");
765 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), start_handle);
766 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPEVALUE.6: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
771 ERR_PRINT(
"invalid att uuid: Unknown exception");
779 if( 0 == pdu->getStartHandle() ) {
780 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
781 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
784 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
785 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
786 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
791 std::unique_ptr<const jau::uuid_t> req_attribute = pdu->getNType();
793 if( req_attribute->equivalent( uuid_characteristic ) ) {
796 }
else if( req_attribute->equivalent( uuid_incl_service ) ) {
804 const uint16_t end_handle = pdu->getEndHandle();
805 const uint16_t start_handle = pdu->getStartHandle();
807 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
818 if( start_handle <= c->getHandle() && c->getHandle() <= end_handle ) {
819 const jau::nsize_t size = 2 + 1 + 2 + c->getValueType()->getTypeSizeInt();
820 if( 0 == rspElemSize ) {
825 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
828 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.2: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
839 ePDUOffset += c->getValueType()->getTypeSizeInt();
848 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.3: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
851 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
852 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.4: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
856 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
857 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.5: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
861 const uint16_t end_handle = pdu->getEndHandle();
862 const uint16_t start_handle = pdu->getStartHandle();
864 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
870 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.6: Searching for %s, req %s from %s",
871 req_attribute->toString().c_str(), pdu->toString().c_str(), gh.toString().c_str());
874 if( start_handle <= c->getHandle() && c->getHandle() <= end_handle && c->getValueType()->equivalent(*req_attribute) ) {
885 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.6: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
890 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
891 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: TYPE.7: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
900 if( 0 == pdu->getStartHandle() ) {
901 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
902 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.0: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
905 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
906 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
907 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.1: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
912 std::unique_ptr<const jau::uuid_t> req_attribute_group = pdu->getNType();
913 uint16_t req_group_type;
914 if( req_attribute_group->equivalent( uuid_prim_service ) ) {
916 }
else if( req_attribute_group->equivalent( uuid_secd_service ) ) {
922 if( 0 != req_group_type ) {
923 const uint16_t end_handle = pdu->getEndHandle();
924 const uint16_t start_handle = pdu->getStartHandle();
926 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
936 start_handle <= s->getHandle() && s->getHandle() <= end_handle )
938 const jau::nsize_t size = 2 + 2 + s->getType()->getTypeSizeInt();
939 if( 0 == rspElemSize ) {
944 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
951 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.3: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
963 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.4: %s -> %s from %s", pdu->toString().c_str(), rsp.
toString().c_str(), gh.toString().c_str());
966 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
967 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.5: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
971 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_GROUP_TYPE, pdu->getOpcode(), pdu->getStartHandle());
972 COND_PRINT(gh.env.DEBUG_DATA,
"GATT-Req: GROUP_TYPE.6: %s -> %s from %s", pdu->toString().c_str(), err.
toString().c_str(), gh.toString().c_str());
982 std::shared_ptr<BTGattHandler> fwd_gh;
987 void close_impl()
noexcept {
988 writeDataQueue.
clear();
989 writeDataQueueHandles.
clear();
994 : gh(gh_), fwdServer(std::move(fwdServer_))
996 fwd_gh = fwdServer->getGattHandler();
1001 void close() noexcept
override { close_impl(); }
1006 if( !fwd_gh->isConnected() ) {
1011 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1012 const uint16_t clientMTU = pdu->getMTUSize();
1014 if(
nullptr == rsp ) {
1015 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1018 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: MTU: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1019 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1020 if( AttPDUMsg::Opcode::EXCHANGE_MTU_RSP == rsp->getOpcode() ) {
1022 const uint16_t serverMTU = mtuRsp->
getMTUSize();
1025 fwd_gh->notifyNativeMTUResponse(clientMTU, *rsp, AttErrorRsp::ErrorCode::NO_ERROR, serverMTU, gh.
getUsedMTU(), clientSource);
1028 static_cast<const AttErrorRsp*
>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1029 fwd_gh->notifyNativeMTUResponse(clientMTU, *rsp, error_code, 0, 0, clientSource);
1031 return gh.
send(*rsp);
1035 if( !fwd_gh->isConnected() ) {
1041 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1043 if( AttPDUMsg::Opcode::PREPARE_WRITE_REQ == pdu->getOpcode() ) {
1046 const uint16_t handle = req->
getHandle();
1049 [&](
const uint16_t it)->bool { return handle == it; }) )
1052 writeDataQueueHandles.
push_back(handle);
1056 if(
nullptr == rsp ) {
1057 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1060 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: WRITE.11: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1061 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1064 static_cast<const AttErrorRsp*
>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1065 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1067 return gh.
send(*rsp);
1068 }
else if( AttPDUMsg::Opcode::EXECUTE_WRITE_REQ == pdu->getOpcode() ) {
1072 for(
auto iter_handle = writeDataQueueHandles.
cbegin(); iter_handle < writeDataQueueHandles.
cend(); ++iter_handle ) {
1076 for(
auto iter_prep_write = writeDataQueue.
cbegin(); iter_prep_write < writeDataQueue.
cend(); ++iter_prep_write ) {
1079 if( handle == *iter_handle ) {
1086 if( p_end > data.
size() ) {
1091 if( sections.
size() > 0 &&
1092 section.
start >= sections[sections.
size()-1].start &&
1096 if( section.
end > sections[sections.
size()-1].
end ) {
1097 sections[sections.
size()-1].
end = section.
end;
1103 if( iter_prep_write + 1 == writeDataQueue.
cend() ) {
1105 fwd_gh->notifyNativeWriteRequest(handle, data, sections,
true , clientSource);
1110 writeDataQueue.
clear();
1111 writeDataQueueHandles.
clear();
1114 if(
nullptr == rsp ) {
1115 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1118 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: WRITE.13: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1119 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1122 static_cast<const AttErrorRsp*
>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1123 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1125 return gh.
send(*rsp);
1128 if( AttPDUMsg::Opcode::WRITE_REQ == pdu->getOpcode() ) {
1135 fwd_gh->notifyNativeWriteRequest(p.
getHandle(), p_val, sections,
true , clientSource);
1138 if(
nullptr == rsp ) {
1139 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1142 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: WRITE.22: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1143 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1146 static_cast<const AttErrorRsp*
>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1147 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1149 return gh.
send(*rsp);
1150 }
else if( AttPDUMsg::Opcode::WRITE_CMD == pdu->getOpcode() ) {
1157 fwd_gh->notifyNativeWriteRequest(p.
getHandle(), p_val, sections,
false , clientSource);
1159 const bool res = fwd_gh->send(*pdu);
1160 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: WRITE.21: res %d, %s to %s", res, pdu->toString().c_str(), fwd_gh->toString().c_str());
1164 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
1166 fwd_gh->notifyNativeReplyReceived(err, clientSource);
1168 fwd_gh->notifyNativeWriteResponse(err, err.
getErrorCode(), clientSource);
1170 return gh.
send(err);
1175 if( !fwd_gh->isConnected() ) {
1180 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1182 uint16_t value_offset;
1184 if( AttPDUMsg::Opcode::READ_REQ == pdu->getOpcode() ) {
1188 }
else if( AttPDUMsg::Opcode::READ_BLOB_REQ == pdu->getOpcode() ) {
1207 if(
nullptr == rsp ) {
1208 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1211 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: READ: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1212 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1214 if( AttPDUMsg::Opcode::READ_RSP == rsp->getOpcode() ||
1215 AttPDUMsg::Opcode::READ_BLOB_RSP == rsp->getOpcode() ) {
1219 fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, AttErrorRsp::ErrorCode::NO_ERROR, p_val, clientSource);
1222 static_cast<const AttErrorRsp*
>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1224 fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, error_code, p_val, clientSource);
1227 return gh.
send(*rsp);
1231 if( !fwd_gh->isConnected() ) {
1236 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1238 if(
nullptr == rsp ) {
1239 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1242 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: INFO: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1243 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1244 return gh.
send(*rsp);
1248 if( !fwd_gh->isConnected() ) {
1253 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1255 if(
nullptr == rsp ) {
1256 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1259 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: TYPEVALUE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1260 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1261 return gh.
send(*rsp);
1265 if( !fwd_gh->isConnected() ) {
1270 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1272 if(
nullptr == rsp ) {
1273 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1276 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1277 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1278 return gh.
send(*rsp);
1282 if( !fwd_gh->isConnected() ) {
1287 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1289 if(
nullptr == rsp ) {
1290 ERR_PRINT2(
"No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1293 COND_PRINT(gh.
env.
DEBUG_DATA,
"GATT-Req: GROUP_TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1294 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1295 return gh.
send(*rsp);
1299std::unique_ptr<BTGattHandler::GattServerHandler> BTGattHandler::selectGattServerHandler(
BTGattHandler& gh,
const DBGattServerRef& gattServerData)
noexcept {
1300 if(
nullptr != gattServerData ) {
1301 switch( gattServerData->getMode() ) {
1302 case DBGattServer::Mode::DB: {
1303 if( gattServerData->getServices().size() > 0 ) {
1304 return std::make_unique<DBGattServerHandler>(gh, gattServerData);
1308 case DBGattServer::Mode::FWD: {
1309 BTDeviceRef fwdServer = gattServerData->getFwdServer();
1310 if(
nullptr != fwdServer ) {
1311 return std::make_unique<FwdGattServerHandler>(gh, fwdServer);
1319 return std::make_unique<NopGattServerHandler>();
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
DBGattServer::Mode getMode() noexcept override
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
void close() noexcept override
Close and clear this handler, i.e.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
~DBGattServerHandler() override
DBGattServerHandler(BTGattHandler &gh_, DBGattServerRef gsd) noexcept
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
FwdGattServerHandler(BTGattHandler &gh_, BTDeviceRef fwdServer_) noexcept
~FwdGattServerHandler() override
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
void close() noexcept override
Close and clear this handler, i.e.
DBGattServer::Mode getMode() noexcept override
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
void close() noexcept override
Close and clear this handler, i.e.
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
NopGattServerHandler() noexcept=default
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
DBGattServer::Mode getMode() noexcept override
jau::nsize_t getElementPDUOffset(const jau::nsize_t elementIdx) const
void setElementCount(const jau::nsize_t count)
Fixate element count.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.1.1 ATT_ERROR_RSP.
constexpr ErrorCode getErrorCode() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.2.1 ATT_EXCHANGE_MTU_REQ BT Core Spec v5....
constexpr uint16_t getMTUSize() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.3 ATT_EXECUTE_WRITE_REQ.
constexpr uint8_t getFlags() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.4 ATT_EXECUTE_WRITE_RSP.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.3 ATT_FIND_BY_TYPE_VALUE_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.4 ATT_FIND_BY_TYPE_VALUE_RSP.
void setElementHandles(const jau::nsize_t elementIdx, const uint16_t handle, const uint16_t handle_end)
void setElementCount(const jau::nsize_t count)
Fixate element count.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.1 ATT_FIND_INFORMATION_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.2 ATT_FIND_INFORMATION_RSP.
void setElementSize(const uint8_t element_length) override
Fixate element length.
void setElementHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementValueUUID(const jau::nsize_t elementIdx, const jau::uuid_t &v)
Handles the Attribute Protocol (ATT) using Protocol Data Unit (PDU) encoded messages over L2CAP chann...
jau::POctets pdu
actual received PDU
virtual std::string toString() const noexcept
constexpr_cxx20 jau::nsize_t getPDUValueSize() const noexcept
Returns the net octet size of this PDU's attributes value, i.e.
constexpr Opcode getOpcode() const noexcept
ATT PDU Format Vol 3, Part F 3.3.1.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.1 ATT_PREPARE_WRITE_REQ BT Core Spec v5....
constexpr uint16_t getValueOffset() const noexcept
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.5 ATT_BLOB_READ_REQ.
constexpr uint16_t getHandle() const noexcept
constexpr uint16_t getValueOffset() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP.
void setElementSize(const uint8_t element_length) override
Fixate element length.
void setElementEndHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementValueUUID(const jau::nsize_t elementIdx, const jau::uuid_t &v)
void setElementStartHandle(const jau::nsize_t elementIdx, const uint16_t h)
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.1 ATT_READ_BY_TYPE_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.2 ATT_READ_BY_TYPE_RSP.
void setElementHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementSize(const uint8_t element_length) override
Fixate element length.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.4 ATT_READ_RSP BT Core Spec v5.2: Vol 3,...
constexpr jau::TOctetSlice const & getValue() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.3 ATT_READ_REQ.
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.3 ATT_WRITE_CMD.
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.1 ATT_WRITE_REQ.
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.2 ATT_WRITE_RSP.
const bool DEBUG_DATA
Debug all GATT Data communication.
Internal handler implementation for given DBGattServer instance matching its DBGattServer::Mode.
A thread safe GATT handler associated to one device via one L2CAP connection.
bool send(const AttPDUMsg &msg) noexcept
Sends the given AttPDUMsg to the connected device via l2cap.
BTDeviceRef getDeviceUnchecked() const noexcept
uint16_t getUsedMTU() const noexcept
std::string toString() const noexcept
void setUsedMTU(const uint16_t mtu) noexcept
uint16_t getServerMTU() const noexcept
const BTGattEnv & env
Environment runtime configuration, usually used internally only.
const jau::fraction_i64 read_cmd_reply_timeout
Derived environment runtime configuration, usually used internally only.
const jau::fraction_i64 write_cmd_reply_timeout
Derived environment runtime configuration, usually used internally only.
std::shared_ptr< Listener > ListenerRef
Mode
Operating mode of a DBGattServer instance.
const std::string & message() const noexcept
Persistent endian aware octet data, i.e.
POctets & resize(const nsize_t newCapacity, const nsize_t newSize)
Resizes this instance, including its capacity.
POctets & recapacity(const nsize_t newCapacity)
Changes the capacity.
constexpr nsize_t capacity() const noexcept
Returns the memory capacity, never zero, greater or equal size().
Transient endian aware octet data slice, i.e.
constexpr lb_endian_t byte_order() const noexcept
Returns byte order of this octet store.
constexpr nsize_t size() const noexcept
constexpr uint8_t const * get_ptr_nc(const nsize_t i) const noexcept
void put_octets_nc(const nsize_t i, const TROOctets &v) noexcept
constexpr void put_uint16_nc(const nsize_t i, const uint16_t v) noexcept
constexpr void put_uint8_nc(const nsize_t i, const uint8_t v) noexcept
void put_bytes(const nsize_t i, const uint8_t *source, const nsize_t byte_count)
uint8_t * get_wptr_nc(const nsize_t i) noexcept
Transient read only and endian aware octet data, i.e.
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 iterator end() noexcept
constexpr const_iterator cend() const noexcept
constexpr size_type size() const noexcept
Like std::vector::size().
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
constexpr reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
constexpr const_iterator cbegin() const noexcept
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
bool equivalent(uuid_t const &o) const noexcept
Relaxed equality operator.
#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 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: '.
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p)
Like std::find_if() of 'algorithm'.
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
@ little
Identifier for little endian, equivalent to endian::little.
std::shared_ptr< BTDevice > BTDeviceRef
std::shared_ptr< DBGattDesc > DBGattDescRef
std::shared_ptr< DBGattService > DBGattServiceRef
std::shared_ptr< DBGattServer > DBGattServerRef
std::shared_ptr< DBGattChar > DBGattCharRef
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.
uint16_t start
start point, inclusive
uint16_t end
end point, exclusive