Skip to content

File syn_ble_gatt.c

File List > ble > syn_ble_gatt.c

Go to the documentation of this file

#include "syn_ble_gatt.h"

#include <string.h>

SYN_Status syn_ble_gatt_init(SYN_BLE_GATT *gatt, const SYN_BLE_GATT_Attr *table, uint16_t table_len,
                             void *user_data)
{
    if (gatt == NULL || (table == NULL && table_len > 0U)) {
        return SYN_INVALID_PARAM;
    }

    memset(gatt, 0, sizeof(*gatt));
    gatt->table = table;
    gatt->table_len = table_len;
    gatt->user_data = user_data;
    return SYN_OK;
}

static const SYN_BLE_GATT_Attr *find_attr(const SYN_BLE_GATT *gatt, uint16_t handle)
{
    for (uint16_t i = 0U; i < gatt->table_len; i++) {
        if (gatt->table[i].handle == handle) {
            return &gatt->table[i];
        }
    }
    return NULL;
}

SYN_Status syn_ble_gatt_process_att_pdu(SYN_BLE_GATT *gatt, uint16_t conn_handle,
                                        const uint8_t *att_pdu, uint16_t pdu_len, uint8_t *resp_buf,
                                        uint16_t *resp_len)
{
    if (gatt == NULL || att_pdu == NULL || resp_buf == NULL || resp_len == NULL || pdu_len < 1U) {
        return SYN_INVALID_PARAM;
    }

    uint8_t opcode = att_pdu[0];

    if (opcode == SYN_BLE_ATT_OP_EXCHANGE_MTU_REQ && pdu_len >= 3U) {
        return syn_ble_att_encode_mtu_rsp(SYN_BLE_ATT_MTU_DEFAULT, resp_buf, resp_len);
    }

    if (opcode == SYN_BLE_ATT_OP_READ_REQ && pdu_len >= 3U) {
        uint16_t handle = (uint16_t)(att_pdu[1] | (att_pdu[2] << 8U));
        const SYN_BLE_GATT_Attr *attr = find_attr(gatt, handle);

        if (attr == NULL) {
            return syn_ble_att_encode_error_rsp(opcode, handle, SYN_BLE_ATT_ERR_ATTR_NOT_FOUND,
                                                resp_buf, resp_len);
        }

        uint8_t val[64];
        uint16_t val_len = 0;
        SYN_Status st = SYN_OK;

        if (attr->read_cb != NULL) {
            st = attr->read_cb(gatt, conn_handle, handle, val, &val_len, gatt->user_data);
        } else if (attr->static_val != NULL) {
            val_len = attr->static_val_len;
            if (val_len > sizeof(val)) {
                val_len = sizeof(val);
            }
            memcpy(val, attr->static_val, val_len);
        } else {
            return syn_ble_att_encode_error_rsp(opcode, handle, SYN_BLE_ATT_ERR_READ_NOT_PERMITTED,
                                                resp_buf, resp_len);
        }

        if (st == SYN_OK) {
            return syn_ble_att_encode_read_rsp(val, val_len, resp_buf, resp_len);
        } else {
            return syn_ble_att_encode_error_rsp(opcode, handle, SYN_BLE_ATT_ERR_UNLIKELY_ERROR,
                                                resp_buf, resp_len);
        }
    }

    if ((opcode == SYN_BLE_ATT_OP_WRITE_REQ || opcode == SYN_BLE_ATT_OP_WRITE_CMD) &&
        pdu_len >= 3U) {
        uint16_t handle = (uint16_t)(att_pdu[1] | (att_pdu[2] << 8U));
        const SYN_BLE_GATT_Attr *attr = find_attr(gatt, handle);

        if (attr == NULL) {
            if (opcode == SYN_BLE_ATT_OP_WRITE_REQ) {
                return syn_ble_att_encode_error_rsp(opcode, handle, SYN_BLE_ATT_ERR_ATTR_NOT_FOUND,
                                                    resp_buf, resp_len);
            }
            *resp_len = 0;
            return SYN_OK;
        }

        const uint8_t *val = &att_pdu[3];
        uint16_t val_len = (uint16_t)(pdu_len - 3U);
        SYN_Status st = SYN_OK;

        if (attr->write_cb != NULL) {
            st = attr->write_cb(gatt, conn_handle, handle, val, val_len, gatt->user_data);
        }

        if (opcode == SYN_BLE_ATT_OP_WRITE_REQ) {
            if (st == SYN_OK) {
                return syn_ble_att_encode_write_rsp(resp_buf, resp_len);
            } else {
                return syn_ble_att_encode_error_rsp(
                    opcode, handle, SYN_BLE_ATT_ERR_WRITE_NOT_PERMITTED, resp_buf, resp_len);
            }
        } else {
            *resp_len = 0;
            return SYN_OK;
        }
    }

    return syn_ble_att_encode_error_rsp(opcode, 0x0000U, SYN_BLE_ATT_ERR_REQ_NOT_SUPPORTED,
                                        resp_buf, resp_len);
}

SYN_Status syn_ble_gatt_notify(SYN_BLE_GATT *gatt, uint16_t conn_handle, uint16_t attr_handle,
                               const uint8_t *val, uint16_t val_len, uint8_t *tx_buf,
                               uint16_t *tx_len)
{
    if (gatt == NULL || tx_buf == NULL || tx_len == NULL) {
        return SYN_INVALID_PARAM;
    }

    uint8_t att_pdu[SYN_BLE_L2CAP_BUF_SIZE];
    uint16_t att_len = 0;
    SYN_Status st = syn_ble_att_encode_notification(attr_handle, val, val_len, att_pdu, &att_len);
    if (st != SYN_OK) {
        return st;
    }

    return syn_ble_l2cap_encode_pdu(conn_handle, SYN_BLE_L2CAP_CID_ATT, att_pdu, att_len, tx_buf,
                                    tx_len);
}