#include #include "ble/att.h" #include "ble/Bluetooth.h" #include namespace trygvis { namespace bluetooth { using namespace std; using std::to_string; std::string to_string(AttPduType t) { switch (t) { case ERROR: return "ERROR"; case EXCHANGE_MTU_REQ: return "EXCHANGE_MTU_REQ"; case EXCHANGE_MTU_RES: return "EXCHANGE_MTU_RES"; case FIND_INFORMATION_REQ: return "FIND_INFORMATION_REQ"; case FIND_INFORMATION_RES: return "FIND_INFORMATION_RES"; case FIND_BY_TYPE_VALUE_REQ: return "FIND_BY_TYPE_VALUE_REQ"; case FIND_BY_TYPE_VALUE_RES: return "FIND_BY_TYPE_VALUE_RES"; case READ_BY_TYPE_REQ: return "READ_BY_TYPE_REQ"; case READ_BY_TYPE_RES: return "READ_BY_TYPE_RES"; case READ_REQ: return "READ_REQ"; case READ_RES: return "READ_RES"; case READ_BLOB_REQ: return "READ_BLOB_REQ"; case READ_BLOB_RES: return "READ_BLOB_RES"; case READ_MULTIPLE_REQ: return "READ_MULTIPLE_REQ"; case READ_MULTIPLE_RES: return "READ_MULTIPLE_RES"; case READ_BY_GROUP_TYPE_REQ: return "READ_BY_GROUP_TYPE_REQ"; case READ_BY_GROUP_TYPE_RES: return "READ_BY_GROUP_TYPE_RES"; case WRITE_REQ: return "WRITE_REQ"; case WRITE_RES: return "WRITE_RES"; case WRITE_CMD: return "WRITE_CMD"; case PREPARE_WRITE_REQ: return "PREPARE_WRITE_REQ"; case PREPARE_WRITE_RES: return "PREPARE_WRITE_RES"; case EXECUTE_WRITE_REQ: return "EXECUTE_WRITE_REQ"; case EXECUTE_WRITE_RES: return "EXECUTE_WRITE_RES"; case HANDLE_VALUE_NOTIFICATION: return "HANDLE_VALUE_NOTIFICATION"; case HANDLE_VALUE_INDICATION: return "HANDLE_VALUE_INDICATION"; case HANDLE_VALUE_CONFIRMATION: return "HANDLE_VALUE_CONFIRMATION"; case SIGNED_WRITE_COMMAND: return "SIGNED_WRITE_COMMAND"; default: return "UNKNOWN"; } } o attPduType(const AttVariant &v) { return visit([](auto &&arg) -> o { using T = std::decay_t; if constexpr (std::is_same_v) { return {}; } else { return {T::att_pdu_type}; } }, v); } AttPdu::AttPdu(ByteBuffer &bytes) : bytes(bytes) { } AttPdu::AttPdu(ByteBuffer &bytes, AttPduType type) : bytes(bytes) { bytes.write8(type); } AttPduType AttPdu::getType() { return (AttPduType) bytes.peek8(0); } void AttPdu::makeExchangeMtuRes(ByteBuffer &bytes, uint16_t serverRxMtu) { bytes.write8(AttPduType::EXCHANGE_MTU_RES). write16le(serverRxMtu); } void AttPdu::makeFindInformationReq(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle) { bytes.write8(AttPduType::FIND_INFORMATION_REQ); bytes.write16le(startHandle); bytes.write16le(endHandle); } void AttPdu::makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid) { bytes.write8(AttPduType::READ_BY_GROUP_TYPE_REQ); bytes.write16le(startHandle); bytes.write16le(endHandle); bytes.write16le(uuid.value); } void AttPdu::makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid) { bytes.write8(AttPduType::READ_BY_TYPE_REQ); bytes.write16le(startHandle); bytes.write16le(endHandle); bytes.write16le(uuid.value); } void AttPdu::makeRead(ByteBuffer &bytes, uint16_t handle) { bytes.write8(AttPduType::READ_REQ); bytes.write16le(handle); } void AttPdu::makeWrite(ByteBuffer &req, uint16_t handle, const ByteBuffer &bytes) { req.write8(AttPduType::WRITE_REQ); req.write16le(handle); req.write(bytes); } vector AttPdu::parseAttributeData(ByteBuffer &bytes) { if (bytes.getSize() < 4) { throw BluetoothException( "Bad READ_BY_GROUP_TYPE_RES packet, expected at least 4 octets, got " + to_string(bytes.getSize())); } uint8_t length = bytes.read8(); size_t count = (bytes.getSize() - 2) / length; vector values; for (int i = 0; i < count; i++) { auto data = bytes.viewForwardAndSkip(length); auto &&x = AttributeData::fromByteBuffer(data); values.emplace_back(x); } return values; } AttVariant AttPdu::parse(ByteBuffer &bytes) { auto type = static_cast(bytes.read8()); switch (type) { case AttPduType::ERROR: return ErrorRes::parse(bytes); case AttPduType::EXCHANGE_MTU_REQ: return parseExchangeMtuReq(bytes); case AttPduType::EXCHANGE_MTU_RES: return parseExchangeMtuRes(bytes); case AttPduType::FIND_INFORMATION_RES: return parseFindInformationRes(bytes); case AttPduType::READ_BY_GROUP_TYPE_RES: return ReadByGroupTypeRes{parseAttributeData(bytes)}; case AttPduType::READ_BY_TYPE_RES: return ReadByTypeRes{parseAttributeData(bytes)}; case AttPduType::HANDLE_VALUE_NOTIFICATION: return parseHandleValueNotification(bytes); case AttPduType::HANDLE_VALUE_INDICATION: return parseHandleValueIndication(bytes); default: return {}; } } ExchangeMtuReq AttPdu::parseExchangeMtuReq(ByteBuffer &bytes) { return {bytes.read16le()}; } ExchangeMtuRes AttPdu::parseExchangeMtuRes(ByteBuffer &bytes) { return {bytes.read16le()}; } FindInformationRes AttPdu::parseFindInformationRes(ByteBuffer &bytes) { auto format = bytes.read8(); std::vector information; if (format == InformationData::FORMAT_SHORT_UUID) { while (bytes.getBytesLeft()) { information.push_back(InformationData::fromByteBufferShortUuid(bytes)); } } else if (format == InformationData::FORMAT_LONG_UUID) { while (bytes.getBytesLeft()) { information.push_back(InformationData::fromByteBufferLongUuid(bytes)); } } return {format, information}; } HandleValueNotification AttPdu::parseHandleValueNotification(ByteBuffer &bytes) { return {bytes.read16le(), bytes.viewCursorToEnd()}; } HandleValueIndication AttPdu::parseHandleValueIndication(ByteBuffer &bytes) { return {bytes.read16le(), bytes.viewCursorToEnd()}; } void AttPdu::parseRead(ByteBuffer &bytes) { auto t = static_cast(bytes.read8()); if (t != READ_RES) { throw BluetoothException("Unexpected type: " + to_string(t) + ", expected " + to_string(READ_RES)); } } void AttPdu::parseWrite(ByteBuffer &bytes) { auto b = bytes.read8(); auto t = static_cast(b); if (t != WRITE_RES) { throw BluetoothException("Unexpected type: " + to_string(b) + "/" + to_string(t) + ", expected " + to_string(WRITE_RES)); } } // ----------------------------------------------------------------------- // AttributeData // ----------------------------------------------------------------------- AttributeData AttributeData::fromByteBuffer(ByteBuffer &bytes) { uint16_t handle = bytes.read16le(); auto raw = make_unique(bytes.getBytesLeft()); bytes.copyTo(raw.get(), bytes.getBytesLeft()); ByteBuffer buffer = ByteBuffer{raw.get(), bytes.getBytesLeft()}; return AttributeData(handle, buffer, std::move(raw)); } AttributeData::AttributeData(uint16_t handle, ByteBuffer value, std::shared_ptr raw) : handle(handle), value(value), raw(std::move(raw)) { } AttributeData::~AttributeData() = default; // ----------------------------------------------------------------------- // InformationData // ----------------------------------------------------------------------- InformationData InformationData::fromByteBufferShortUuid(ByteBuffer &value) { return {value.read16le(), ShortUuid(value).toLong()}; } InformationData InformationData::fromByteBufferLongUuid(ByteBuffer &value) { return {value.read16le(), Uuid(value)}; } InformationData::InformationData(uint16_t handle, const Uuid &uuid) : handle(handle), uuid(uuid) {} } // namespace bluetooth } // namespace trygvis