[PATCH v4 2/4] src/shared/att: Implement basic boilerplate.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch implements the getters, setters, creation, ref, and unref
functions for struct bt_att. Also added is a simple table for
determining the ATT op type given an opcode and the io read handler that
currently does nothing.
---
 src/shared/att.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 293 insertions(+), 21 deletions(-)

diff --git a/src/shared/att.c b/src/shared/att.c
index fc75eb8..1c11cd7 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -25,15 +25,19 @@
 #include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <unistd.h>
 
 #include "src/shared/io.h"
 #include "src/shared/queue.h"
+#include "src/shared/util.h"
 #include "lib/uuid.h"
 #include "src/shared/att.h"
 
 #define ATT_DEFAULT_LE_MTU		23
 #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
 
 struct att_send_op;
 
@@ -42,7 +46,6 @@ struct bt_att {
 	int fd;
 	bool close_on_unref;
 	struct io *io;
-	bool destroyed;
 
 	struct queue *req_queue;	/* Queued ATT protocol requests */
 	struct att_send_op *pending_req;
@@ -51,7 +54,7 @@ struct bt_att {
 	struct queue *write_queue;	/* Queue of PDUs ready to send */
 	bool writer_active;
 
-	/* TODO Add notify queue */
+	bool invalid;	/* bt_att becomes invalid, when a request times out */
 
 	uint8_t *buf;
 	uint16_t mtu;
@@ -73,8 +76,65 @@ struct bt_att {
 	void *debug_data;
 };
 
+enum att_op_type {
+	ATT_OP_TYPE_REQ,
+	ATT_OP_TYPE_RSP,
+	ATT_OP_TYPE_CMD,
+	ATT_OP_TYPE_IND,
+	ATT_OP_TYPE_NOT,
+	ATT_OP_TYPE_CONF,
+	ATT_OP_TYPE_UNKNOWN,
+};
+
+static const struct {
+	uint8_t opcode;
+	enum att_op_type type;
+} att_opcode_type_table[] = {
+	{ BT_ATT_OP_ERROR_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_MTU_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_MTU_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_FIND_INFO_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_FIND_INFO_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,	ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_FIND_BY_TYPE_VAL_RSP,	ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BY_TYPE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BY_TYPE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BLOB_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BLOB_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_MULT_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_MULT_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_READ_BY_GRP_TYPE_REQ,	ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_READ_BY_GRP_TYPE_RSP,	ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_WRITE_REQ,			ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_WRITE_RSP,			ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_WRITE_CMD,			ATT_OP_TYPE_CMD },
+	{ BT_ATT_OP_SIGNED_WRITE_CMD,		ATT_OP_TYPE_CMD },
+	{ BT_ATT_OP_PREP_WRITE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_PREP_WRITE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_EXEC_WRITE_REQ,		ATT_OP_TYPE_REQ },
+	{ BT_ATT_OP_EXEC_WRITE_RSP,		ATT_OP_TYPE_RSP },
+	{ BT_ATT_OP_HANDLE_VAL_NOT,		ATT_OP_TYPE_NOT },
+	{ BT_ATT_OP_HANDLE_VAL_IND,		ATT_OP_TYPE_IND },
+	{ BT_ATT_OP_HANDLE_VAL_CONF,		ATT_OP_TYPE_CONF },
+	{ }
+};
+
+static enum att_op_type get_op_type(uint8_t opcode)
+{
+	int i;
+	for (i = 0; att_opcode_type_table[i].opcode; i++) {
+		if (att_opcode_type_table[i].opcode == opcode)
+			return att_opcode_type_table[i].type;
+	}
+
+	return ATT_OP_TYPE_UNKNOWN;
+}
+
 struct att_send_op {
 	unsigned int id;
+	enum att_op_type type;
 	uint16_t opcode;
 	void *pdu;
 	uint16_t len;
@@ -83,61 +143,219 @@ struct att_send_op {
 	void *user_data;
 };
 
+static void destroy_att_send_op(void *data)
+{
+	struct att_send_op *op = data;
+
+	if (op->destroy)
+		op->destroy(op->user_data);
+
+	free(op->pdu);
+	free(op);
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+	struct bt_att *att = user_data;
+	uint8_t *pdu;
+	ssize_t bytes_read;
+
+	bytes_read = read(att->fd, att->buf, att->mtu);
+	if (bytes_read < 0)
+		return false;
+
+	util_hexdump('>', att->buf, bytes_read,
+					att->debug_callback, att->debug_data);
+
+	if (bytes_read < ATT_MIN_PDU_LEN)
+		return true;
+
+	/* TODO: Handle different types of PDUs here */
+	return true;
+}
+
 struct bt_att *bt_att_new(int fd)
 {
-	/* TODO */
+	struct bt_att *att;
+
+	if (fd < 0)
+		return NULL;
+
+	att = new0(struct bt_att, 1);
+	if (!att)
+		return NULL;
+
+	att->fd = fd;
+
+	att->mtu = ATT_DEFAULT_LE_MTU;
+	att->buf = malloc(att->mtu);
+	if (!att->buf)
+		goto fail;
+
+	att->io = io_new(fd);
+	if (!att->io)
+		goto fail;
+
+	att->req_queue = queue_new();
+	if (!att->req_queue)
+		goto fail;
+
+	att->ind_queue = queue_new();
+	if (!att->ind_queue)
+		goto fail;
+
+	att->write_queue = queue_new();
+	if (!att->write_queue)
+		goto fail;
+
+	if (!io_set_read_handler(att->io, can_read_data, att, NULL))
+		goto fail;
+
+	return bt_att_ref(att);
+
+fail:
+	queue_destroy(att->req_queue, NULL);
+	queue_destroy(att->ind_queue, NULL);
+	queue_destroy(att->write_queue, NULL);
+	io_destroy(att->io);
+	free(att->buf);
+	free(att);
+
 	return NULL;
 }
 
 struct bt_att *bt_att_ref(struct bt_att *att)
 {
-	/* TODO */
-	return NULL;
+	if (!att)
+		return NULL;
+
+	__sync_fetch_and_add(&att->ref_count, 1);
+
+	return att;
 }
 
 void bt_att_unref(struct bt_att *att)
 {
-	/* TODO */
+	if (!att)
+		return;
+
+	if (__sync_sub_and_fetch(&att->ref_count, 1))
+		return;
+
+	bt_att_cancel_all(att);
+
+	io_set_write_handler(att->io, NULL, NULL, NULL);
+	io_set_read_handler(att->io, NULL, NULL, NULL);
+
+	queue_destroy(att->req_queue, NULL);
+	queue_destroy(att->ind_queue, NULL);
+	queue_destroy(att->write_queue, NULL);
+	att->req_queue = NULL;
+	att->ind_queue = NULL;
+	att->write_queue = NULL;
+
+	io_destroy(att->io);
+	att->io = NULL;
+
+	if (att->close_on_unref)
+		close(att->fd);
+
+	if (att->timeout_destroy)
+		att->timeout_destroy(att->timeout_data);
+
+	if (att->debug_destroy)
+		att->debug_destroy(att->debug_data);
+
+	free(att->buf);
+	att->buf = NULL;
+
+	free(att);
 }
 
 bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close)
 {
-	/* TODO */
-	return false;
+	if (!att)
+		return false;
+
+	att->close_on_unref = do_close;
+
+	return true;
 }
 
 bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
 				void *user_data, bt_att_destroy_func_t destroy)
 {
-	/* TODO */
-	return false;
+	if (!att)
+		return false;
+
+	if (att->debug_destroy)
+		att->debug_destroy(att->debug_data);
+
+	att->debug_callback = callback;
+	att->debug_destroy = destroy;
+	att->debug_data = user_data;
+
+	return true;
 }
 
 uint16_t bt_att_get_mtu(struct bt_att *att)
 {
-	/* TODO */
-	return 0;
+	if (!att)
+		return 0;
+
+	return att->mtu;
 }
 
 bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu)
 {
-	/* TODO */
-	return false;
+	char *buf;
+
+	if (!att)
+		return false;
+
+	if (mtu < ATT_DEFAULT_LE_MTU)
+		return false;
+
+	buf = malloc(mtu);
+	if (!buf)
+		return false;
+
+	free(att->buf);
+
+	att->mtu = mtu;
+	att->buf = buf;
+
+	return true;
 }
 
 void bt_att_set_signing_data(struct bt_att *att, uint8_t csrk[16],
 						uint32_t local_sign_cnt,
 						uint32_t remote_sign_cnt)
 {
-	/* TODO */
+	if (!att)
+		return;
+
+	memcpy(att->csrk, csrk, sizeof(csrk));
+	att->local_sign_cnt = local_sign_cnt;
+	att->remote_sign_cnt = remote_sign_cnt;
+	att->signing_data_set = true;
 }
 
 bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback,
 						void *user_data,
 						bt_att_destroy_func_t destroy)
 {
-	/* TODO */
-	return false;
+	if (!att)
+		return false;
+
+	if (att->timeout_destroy)
+		att->timeout_destroy(att->timeout_data);
+
+	att->timeout_callback = callback;
+	att->timeout_destroy = destroy;
+	att->timeout_data = user_data;
+
+	return true;
 }
 
 unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
@@ -149,16 +367,70 @@ unsigned int bt_att_send(struct bt_att *att, uint8_t opcode,
 	return 0;
 }
 
+static bool match_op_id(const void *a, const void *b)
+{
+	const struct att_send_op *op = a;
+	unsigned int id = PTR_TO_UINT(b);
+
+	return op->id == id;
+}
+
 bool bt_att_cancel(struct bt_att *att, unsigned int id)
 {
-	/* TODO */
-	return false;
+	struct att_send_op *op;
+
+	if (!att || !id)
+		return false;
+
+	if (att->pending_req && att->pending_req->id == id) {
+		op = att->pending_req;
+		goto done;
+	}
+
+	if (att->pending_ind && att->pending_ind->id == id) {
+		op = att->pending_ind;
+		goto done;
+	}
+
+	op = queue_remove_if(att->req_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	op = queue_remove_if(att->ind_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	op = queue_remove_if(att->write_queue, match_op_id, UINT_TO_PTR(id));
+	if (op)
+		goto done;
+
+	if (!op)
+		return false;
+
+done:
+	destroy_att_send_op(op);
+
+	/* TODO: Set the write handler here */
+
+	return true;
 }
 
 bool bt_att_cancel_all(struct bt_att *att)
 {
-	/* TODO */
-	return false;
+	if (!att)
+		return false;
+
+	queue_remove_all(att->req_queue, NULL, NULL, destroy_att_send_op);
+	queue_remove_all(att->ind_queue, NULL, NULL, destroy_att_send_op);
+	queue_remove_all(att->write_queue, NULL, NULL, destroy_att_send_op);
+
+	if (att->pending_req)
+		destroy_att_send_op(att->pending_req);
+
+	if (att->pending_ind)
+		destroy_att_send_op(att->pending_ind);
+
+	return true;
 }
 
 unsigned int bt_att_register(struct bt_att *att, uint8_t opcode,
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux