aboutsummaryrefslogtreecommitdiff
path: root/ble/att.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ble/att.cpp')
-rw-r--r--ble/att.cpp212
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