diff options
Diffstat (limited to 'ble/att.cpp')
-rw-r--r-- | ble/att.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/ble/att.cpp b/ble/att.cpp new file mode 100644 index 0000000..585e914 --- /dev/null +++ b/ble/att.cpp @@ -0,0 +1,212 @@ +#include <ble/att.h> + +#include "ble/att.h" +#include "ble/Bluetooth.h" +#include <iostream> + +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> attPduType(const AttVariant &v) { + return visit([](auto &&arg) -> o<AttPduType>{ + using T = std::decay_t<decltype(arg)>; + + if constexpr (std::is_same_v<T, std::monostate>) { + 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::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<AttributeData> 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<AttributeData> values; + for (int i = 0; i < count; i++) { + auto data = bytes.viewAndSkip(length); + auto&& x = AttributeData::fromByteBuffer(data); + values.emplace_back(x); + } + + return values; +} + +AttVariant AttPdu::parse(ByteBuffer &bytes) { + switch (static_cast<AttPduType>(bytes.read8())) { + 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::READ_BY_GROUP_TYPE_RES: + return ReadByGroupTypeRes{parseAttributeData(bytes)}; + case AttPduType::READ_BY_TYPE_RES: + return ReadByTypeRes{parseAttributeData(bytes)}; + default: + return {}; + } +} + +ExchangeMtuReq AttPdu::parseExchangeMtuReq(ByteBuffer &bytes) { + return {bytes.read16le()}; +} + +ExchangeMtuRes AttPdu::parseExchangeMtuRes(ByteBuffer &bytes) { + return {bytes.read16le()}; +} + +void AttPdu::parseRead(ByteBuffer &bytes) { + auto t = static_cast<AttPduType>(bytes.read8()); + + if (t != READ_RES) { + throw BluetoothException("Unexpected type: " + to_string(t) + ", expected " + to_string(READ_RES)); + } +} + +void AttPdu::parseWrite(ByteBuffer &bytes) { + auto t = static_cast<AttPduType>(bytes.read8()); + + if (t != WRITE_RES) { + throw BluetoothException("Unexpected type: " + to_string(t) + ", expected " + to_string(WRITE_RES)); + } +} + +// ----------------------------------------------------------------------- +// AttributeData +// ----------------------------------------------------------------------- + +AttributeData AttributeData::fromByteBuffer(ByteBuffer &bytes) { + uint16_t handle = bytes.read16le(); + + auto raw = make_unique<uint8_t[]>(bytes.getBytesLeft()); + + bytes.copy(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<uint8_t[]> raw) : + handle(handle), value(value), raw(std::move(raw)) { +} + +AttributeData::~AttributeData() = default; + +} // namespace bluetooth +} // namespace trygvis |