diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 24bf3da..3d8829a 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
#include <stdint.h>
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
#define BT_ATT_DEFAULT_LE_MTU 23
/* ATT protocol opcodes */
#define BT_ATT_OP_HANDLE_VAL_IND 0x1D
#define BT_ATT_OP_HANDLE_VAL_CONF 0x1E
+/* Packed struct definitions for ATT protocol PDUs */
+/* TODO: Complete these definitions for all opcodes */
+struct bt_att_pdu_error_rsp {
+ uint8_t opcode;
+ uint16_t handle;
+ uint8_t ecode;
+} __packed;
+
/* Special opcode to receive all requests (legacy servers) */
#define BT_ATT_ALL_REQUESTS 0x00
diff --git a/src/shared/att.c b/src/shared/att.c
index 6e1e538..2bc7682 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
#include "src/shared/timeout.h"
#include "lib/uuid.h"
#include "src/shared/att.h"
-#include "src/shared/att-types.h"
#define ATT_MIN_PDU_LEN 1 /* At least 1 byte for the opcode. */
#define ATT_OP_CMD_MASK 0x40
#define ATT_OP_SIGNED_MASK 0x80
#define ATT_TIMEOUT_INTERVAL 30000 /* 30000 ms */
+/*
+ * Common Profile and Service Error Code descriptions (see Supplement to the
+ * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
+ * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
+ * following:
+ */
+#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
+#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
+#define BT_ERROR_OUT_OF_RANGE 0xff
+
struct att_send_op;
struct bt_att {
return true;
}
+static uint8_t att_ecode_from_error(int err)
+{
+ /*
+ * If the error fits in a single byte, treat it as an ATT protocol
+ * error as is. Since "0" is not a valid ATT protocol error code, we map
+ * that to UNLIKELY below.
+ */
+ if (err > 0 && err < UINT8_MAX)
+ return err;
+
+ /*
+ * Since we allow UNIX errnos, map them to appropriate ATT protocol
+ * and "Common Profile and Service" error codes.
+ */
+ switch (err) {
+ case -ENOENT:
+ return BT_ATT_ERROR_INVALID_HANDLE;
+ case -ENOMEM:
+ return BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ case -EALREADY:
+ return BT_ERROR_ALREADY_IN_PROGRESS;
+ case -EOVERFLOW:
+ return BT_ERROR_OUT_OF_RANGE;
+ }
+
+ return BT_ATT_ERROR_UNLIKELY;
+}
+
+unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+ uint16_t handle, int error)
+{
+ struct bt_att_pdu_error_rsp pdu;
+ uint8_t ecode;
+
+ if (!att || !opcode)
+ return 0;
+
+ ecode = att_ecode_from_error(error);
+
+ memset(&pdu, 0, sizeof(pdu));
+
+ pdu.opcode = opcode;
+ put_le16(handle, &pdu.handle);
+ pdu.ecode = ecode;
+
+ return bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu),
+ NULL, NULL, NULL);
+}
+
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
bt_att_notify_func_t callback,
void *user_data,
diff --git a/src/shared/att.h b/src/shared/att.h
index 1063021..99b5a5b 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
bool bt_att_cancel(struct bt_att *att, unsigned int id);
bool bt_att_cancel_all(struct bt_att *att);
+unsigned int bt_att_send_error_rsp(struct bt_att *att, uint8_t opcode,
+ uint16_t handle, int error);
+
unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
bt_att_notify_func_t callback,
void *user_data,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 30b271e..1f82048 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
static uint8_t process_error(const void *pdu, uint16_t length)
{
- if (!pdu || length != 4)
+ const struct bt_att_pdu_error_rsp *error_pdu;
+
+ if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp))
return 0;
- return ((uint8_t *) pdu)[3];
+ error_pdu = pdu;
+
+ return error_pdu->ecode;
}
static void enable_ccc_callback(uint8_t opcode, const void *pdu,
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 220d0eb..9c8a3ba 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
static uint8_t process_error(const void *pdu, uint16_t length)
{
- if (!pdu || length != 4)
+ const struct bt_att_pdu_error_rsp *error_pdu;
+
+ if (!pdu || length != sizeof(struct bt_att_pdu_error_rsp))
return 0;
- return ((uint8_t *) pdu)[3];
+ error_pdu = pdu;
+
+ return error_pdu->ecode;
}
static void mtu_cb(uint8_t opcode, const void *pdu, uint16_t length,
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 2ca318a..2ef9269 100644
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
*/
#include <sys/uio.h>
+#include <errno.h>
#include "src/shared/att.h"
#include "lib/uuid.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
#include "src/shared/gatt-helpers.h"
-#include "src/shared/att-types.h"
#include "src/shared/util.h"
#ifndef MAX
free(server);
}
-static uint8_t att_ecode_from_error(int err)
-{
- if (err < 0 || err > UINT8_MAX)
- return 0xff;
-
- return err;
-}
-
-static void encode_error_rsp(uint8_t opcode, uint16_t handle, uint8_t ecode,
- uint8_t pdu[4])
-{
- pdu[0] = opcode;
- pdu[3] = ecode;
- put_le16(handle, pdu + 1);
-}
-
static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid)
{
uint128_t u128;
uint16_t mtu = bt_att_get_mtu(server->att);
uint8_t rsp_pdu[mtu];
uint16_t rsp_len;
- uint8_t rsp_opcode;
uint8_t ecode = 0;
uint16_t ehandle = 0;
struct queue *q = NULL;
goto error;
}
- rsp_opcode = BT_ATT_OP_READ_BY_GRP_TYPE_RSP;
+ queue_destroy(q, NULL);
- goto done;
+ bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP,
+ rsp_pdu, rsp_len,
+ NULL, NULL, NULL);
-error:
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
+ return;
-done:
+error:
queue_destroy(q, NULL);
- bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len,
- NULL, NULL, NULL);
+ bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
}
static void async_read_op_destroy(struct async_read_op *op)
/* Terminate the operation if there was an error */
if (err) {
- uint8_t pdu[4];
- uint8_t att_ecode = att_ecode_from_error(err);
-
- encode_error_rsp(BT_ATT_OP_READ_BY_TYPE_REQ, handle, att_ecode,
- pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, pdu, 4, NULL,
- NULL, NULL);
+ bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ handle, err);
async_read_op_destroy(op);
return;
}
static void process_read_by_type(struct async_read_op *op)
{
struct bt_gatt_server *server = op->server;
- uint8_t rsp_opcode;
- uint8_t rsp_len;
uint8_t ecode;
- uint16_t ehandle;
struct gatt_db_attribute *attr;
uint32_t perm;
attr = queue_pop_head(op->db_data);
if (op->done || !attr) {
- rsp_opcode = BT_ATT_OP_READ_BY_TYPE_RSP;
- rsp_len = op->pdu_len;
- goto done;
+ bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu,
+ op->pdu_len,
+ NULL, NULL,
+ NULL);
+ async_read_op_destroy(op);
+ return;
}
if (!gatt_db_attribute_get_permissions(attr, &perm)) {
ecode = BT_ATT_ERROR_UNLIKELY;
error:
- ehandle = gatt_db_attribute_get_handle(attr);
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- encode_error_rsp(BT_ATT_OP_READ_BY_TYPE_REQ, ehandle, ecode, op->pdu);
-
-done:
- bt_att_send(server->att, rsp_opcode, op->pdu, rsp_len, NULL,
- NULL, NULL);
+ bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ gatt_db_attribute_get_handle(attr), ecode);
async_read_op_destroy(op);
}
struct bt_gatt_server *server = user_data;
uint16_t start, end;
bt_uuid_t type;
- uint8_t rsp_pdu[4];
uint16_t ehandle = 0;
uint8_t ecode;
struct queue *q = NULL;
return;
error:
- encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
+ bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
queue_destroy(q, NULL);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu, 4,
- NULL, NULL, NULL);
}
static void put_uuid_le(const bt_uuid_t *src, void *dst)
uint16_t mtu = bt_att_get_mtu(server->att);
uint8_t rsp_pdu[mtu];
uint16_t rsp_len;
- uint8_t rsp_opcode;
uint8_t ecode = 0;
uint16_t ehandle = 0;
struct queue *q = NULL;
goto error;
}
- rsp_opcode = BT_ATT_OP_FIND_INFO_RSP;
+ bt_att_send(server->att, BT_ATT_OP_FIND_INFO_RSP, rsp_pdu, rsp_len,
+ NULL, NULL, NULL);
+ queue_destroy(q, NULL);
- goto done;
+ return;
error:
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- encode_error_rsp(opcode, ehandle, ecode, rsp_pdu);
-
-done:
+ bt_att_send_error_rsp(server->att, opcode, ehandle, ecode);
queue_destroy(q, NULL);
- bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len,
- NULL, NULL, NULL);
+
}
static void async_write_op_destroy(struct async_write_op *op)
handle = gatt_db_attribute_get_handle(attr);
- if (err) {
- uint8_t rsp_pdu[4];
- uint8_t att_ecode = att_ecode_from_error(err);
-
- encode_error_rsp(op->opcode, handle, att_ecode, rsp_pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu, 4,
- NULL, NULL, NULL);
- } else {
+ if (err)
+ bt_att_send_error_rsp(server->att, op->opcode, handle, err);
+ else
bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0,
NULL, NULL, NULL);
- }
async_write_op_destroy(op);
}
struct bt_gatt_server *server = user_data;
struct gatt_db_attribute *attr;
uint16_t handle = 0;
- uint8_t rsp_pdu[4];
struct async_write_op *op = NULL;
uint8_t ecode;
uint32_t perm;
if (opcode == BT_ATT_OP_WRITE_CMD)
return;
- encode_error_rsp(opcode, handle, ecode, rsp_pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu, 4,
- NULL, NULL, NULL);
+ bt_att_send_error_rsp(server->att, opcode, handle, ecode);
}
static uint8_t get_read_rsp_opcode(uint8_t opcode)
handle = gatt_db_attribute_get_handle(attr);
if (err) {
- uint8_t pdu[4];
- uint8_t att_ecode = att_ecode_from_error(err);
-
- encode_error_rsp(op->opcode, handle, att_ecode, pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, pdu, 4, NULL,
- NULL, NULL);
+ bt_att_send_error_rsp(server->att, op->opcode, handle, err);
async_read_op_destroy(op);
return;
}
uint16_t handle,
uint16_t offset)
{
- uint8_t error_pdu[4];
struct gatt_db_attribute *attr;
uint8_t ecode;
uint32_t perm;
if (op)
async_read_op_destroy(op);
- encode_error_rsp(opcode, handle, ecode, error_pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, error_pdu, 4, NULL, NULL,
- NULL);
+ bt_att_send_error_rsp(server->att, opcode, handle, ecode);
}
static void read_cb(uint8_t opcode, const void *pdu,
uint16_t handle;
if (length != 2) {
- uint8_t pdu[4];
-
- encode_error_rsp(opcode, 0, BT_ATT_ERROR_INVALID_PDU, pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, pdu, 4, NULL,
- NULL, NULL);
+ bt_att_send_error_rsp(server->att, opcode, 0,
+ BT_ATT_ERROR_INVALID_PDU);
return;
}
uint16_t handle, offset;
if (length != 4) {
- uint8_t pdu[4];
-
- encode_error_rsp(opcode, 0, BT_ATT_ERROR_INVALID_PDU, pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, pdu, 4, NULL,
- NULL, NULL);
+ bt_att_send_error_rsp(server->att, opcode, 0,
+ BT_ATT_ERROR_INVALID_PDU);
return;
}
uint16_t handle = 0;
uint16_t offset;
struct gatt_db_attribute *attr;
- uint8_t rsp_opcode;
- uint8_t rsp_pdu[MAX(4, length)];
- uint16_t rsp_len;
uint8_t ecode;
uint32_t perm;
queue_push_tail(server->prep_queue, prep_data);
- /* Create the response */
- rsp_len = length;
- rsp_opcode = BT_ATT_OP_PREP_WRITE_RSP;
- memcpy(rsp_pdu, pdu, rsp_len);
-
- goto done;
+ bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL,
+ NULL, NULL);
+ return;
error:
if (prep_data)
prep_write_data_destroy(prep_data);
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- encode_error_rsp(opcode, handle, ecode, rsp_pdu);
+ bt_att_send_error_rsp(server->att, opcode, handle, ecode);
-done:
- bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len, NULL, NULL,
- NULL);
}
static void exec_next_prep_write(struct bt_gatt_server *server,
- uint16_t ehandle, uint8_t att_ecode);
+ uint16_t ehandle, int err);
static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err,
void *user_data)
{
struct bt_gatt_server *server = user_data;
uint16_t handle = gatt_db_attribute_get_handle(attr);
- uint16_t att_ecode = att_ecode_from_error(err);
- exec_next_prep_write(server, handle, att_ecode);
+ exec_next_prep_write(server, handle, err);
}
static void exec_next_prep_write(struct bt_gatt_server *server,
- uint16_t ehandle, uint8_t att_ecode)
+ uint16_t ehandle, int err)
{
struct prep_write_data *next = NULL;
- uint8_t rsp_opcode = BT_ATT_OP_EXEC_WRITE_RSP;
- uint8_t error_pdu[4];
- uint8_t *rsp_pdu = NULL;
- uint16_t rsp_len = 0;
struct gatt_db_attribute *attr;
bool status;
- if (att_ecode)
+ if (err)
goto error;
next = queue_pop_head(server->prep_queue);
- if (!next)
- goto done;
+ if (!next) {
+ bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
+ NULL, NULL, NULL);
+ return;
+ }
attr = gatt_db_get_attribute(server->db, next->handle);
if (!attr) {
- att_ecode = BT_ATT_ERROR_UNLIKELY;
+ err = BT_ATT_ERROR_UNLIKELY;
goto error;
}
if (status)
return;
- att_ecode = BT_ATT_ERROR_UNLIKELY;
+ err = BT_ATT_ERROR_UNLIKELY;
error:
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- rsp_pdu = error_pdu;
- encode_error_rsp(BT_ATT_OP_EXEC_WRITE_REQ, ehandle, att_ecode, rsp_pdu);
-
-done:
- bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len, NULL, NULL,
- NULL);
+ bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ ehandle, err);
}
static void exec_write_cb(uint8_t opcode, const void *pdu,
struct bt_gatt_server *server = user_data;
uint8_t flags;
uint8_t ecode;
- uint8_t rsp_opcode;
- uint8_t error_pdu[4];
- uint8_t *rsp_pdu = NULL;
- uint16_t rsp_len = 0;
bool write;
if (length != 1) {
if (!write) {
queue_remove_all(server->prep_queue, NULL, NULL,
prep_write_data_destroy);
- rsp_opcode = BT_ATT_OP_EXEC_WRITE_RSP;
- goto done;
+ bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0,
+ NULL, NULL, NULL);
+ return;
}
exec_next_prep_write(server, 0, 0);
return;
error:
- rsp_opcode = BT_ATT_OP_ERROR_RSP;
- rsp_len = 4;
- rsp_pdu = error_pdu;
- encode_error_rsp(opcode, 0, ecode, rsp_pdu);
-
-done:
- bt_att_send(server->att, rsp_opcode, rsp_pdu, rsp_len, NULL, NULL,
- NULL);
+ bt_att_send_error_rsp(server->att, opcode, 0, ecode);
}
static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
struct bt_gatt_server *server = user_data;
uint16_t client_rx_mtu;
uint16_t final_mtu;
- uint8_t rsp_pdu[4];
+ uint8_t rsp_pdu[2];
if (length != 2) {
- encode_error_rsp(opcode, 0, BT_ATT_ERROR_INVALID_PDU, rsp_pdu);
- bt_att_send(server->att, BT_ATT_OP_ERROR_RSP, rsp_pdu,
- sizeof(rsp_pdu), NULL, NULL, NULL);
+ bt_att_send_error_rsp(server->att, opcode, 0,
+ BT_ATT_ERROR_INVALID_PDU);
return;
}