This patch set contains gatt characteristic and descriptors handling.
It start with gatt status reporting fix, as expected by Android GATT_FAILURE
does not map directly to HAL_STATUS.
Next two v2 patches are Gregorz's patches for characteristic read and write
rebased on top of the gatt status reporting fix.
Next goes descriptors get, read, write handling. The last patch is just
simple cleanup.
Grzegorz Kolodziejczyk (2):
android/gatt: Handle read characteristic client command
android/gatt: Handle write characteristic client command
Jakub Tyszkowski (7):
android/gatt: Use proper GATT status
android/gatt: Add queue for characteristic descriptors caching
android/gatt: Handle get descriptor client command
android/gatt: Search for descriptors and notify
android/gatt: Add client read descriptor handler
android/gatt: Add client write descriptor handler
android/gatt: Simplify matching functions
android/gatt.c | 805 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 777 insertions(+), 28 deletions(-)
--
1.9.0
---
android/gatt.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 4dd16e7..be38f66 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -62,9 +62,16 @@ struct element_id {
uint8_t instance;
};
+struct descriptor {
+ struct element_id id;
+ uint16_t handle;
+};
+
struct characteristic {
struct element_id id;
struct gatt_char ch;
+
+ struct queue *descriptors;
};
struct service {
@@ -135,6 +142,17 @@ static void uuid2android(const bt_uuid_t *src, uint8_t *uuid)
uuid[15 - i] = src->value.u128.data[i];
}
+static void destroy_characteristic(void *data)
+{
+ struct characteristic *chars = data;
+
+ if (!chars)
+ return;
+
+ queue_destroy(chars->descriptors, free);
+ free(chars);
+}
+
static void destroy_service(void *data)
{
struct service *srvc = data;
@@ -142,7 +160,7 @@ static void destroy_service(void *data)
if (!srvc)
return;
- queue_destroy(srvc->chars, free);
+ queue_destroy(srvc->chars, destroy_characteristic);
free(srvc);
}
@@ -1237,6 +1255,12 @@ static void cache_all_srvc_chars(GSList *characteristics, struct queue *q)
continue;
}
+ ch->descriptors = queue_new();
+ if (!ch->descriptors) {
+ free(ch);
+ continue;
+ }
+
memcpy(&ch->ch, characteristics->data, sizeof(ch->ch));
bt_string_to_uuid(&uuid, ch->ch.uuid);
--
1.9.0
---
android/gatt.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 135 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 1fe7035..0a4159e 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -49,6 +49,8 @@
#include "attrib/gatt.h"
#include "btio/btio.h"
+#define GATT_MAX_ATTR_LEN 600
+
#define GATT_SUCCESS 0x00000000
#define GATT_FAILURE 0x00000101
@@ -237,6 +239,17 @@ static bool match_char_by_higher_inst_id(const void *data,
return inst_id < ch->id.instance;
}
+static bool match_descr_by_element_id(const void *data, const void *user_data)
+{
+ const struct element_id *exp_id = user_data;
+ const struct descriptor *descr = data;
+
+ if (exp_id->instance == descr->id.instance)
+ return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid);
+
+ return false;
+}
+
static bool match_descr_by_higher_inst_id(const void *data,
const void *user_data)
{
@@ -1861,12 +1874,133 @@ done:
HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
}
+static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu,
+ guint16 len, int32_t conn_id,
+ const struct descriptor *descr,
+ const struct service *srvc,
+ const struct characteristic *ch)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->status = status;
+ ev->conn_id = conn_id;
+
+ if (srvc) {
+ ev->data.srvc_id.inst_id = srvc->id.instance;
+ uuid2android(&srvc->id.uuid, ev->data.srvc_id.uuid);
+ }
+
+ if (ch) {
+ ev->data.char_id.inst_id = ch->id.instance;
+ uuid2android(&ch->id.uuid, ev->data.char_id.uuid);
+ }
+
+ if (descr) {
+ ev->data.descr_id.inst_id = descr->id.instance;
+ uuid2android(&descr->id.uuid, ev->data.descr_id.uuid);
+ }
+
+ if (pdu)
+ ev->data.len = dec_read_resp(pdu, len, ev->data.value,
+ GATT_MAX_ATTR_LEN);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_READ_DESCRIPTOR,
+ sizeof(*ev) + ev->data.len, ev);
+}
+
+struct read_desc_data {
+ int32_t conn_id;
+ struct service *service;
+ struct characteristic *characteristic;
+ struct descriptor *descriptor;
+};
+
+static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct read_desc_data *cb_data = user_data;
+
+ if (status != 0)
+ error("gatt: Discover all char descriptors failed: %s\n",
+ att_ecode2str(status));
+
+ send_client_descr_read_notify(status, pdu, len, cb_data->conn_id,
+ cb_data->descriptor, cb_data->service,
+ cb_data->characteristic);
+
+ free(cb_data);
+}
+
static void handle_client_read_descriptor(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_read_descriptor *cmd = buf;
+ struct characteristic *ch = NULL;
+ struct descriptor *descr = NULL;
+ struct read_desc_data *cb_data;
+ struct service *srvc = NULL;
+ struct element_id match_id;
+ struct gatt_device *dev;
+ int32_t conn_id = 0;
+ uint8_t status;
+
DBG("");
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
+ error("gatt: Read descr. could not find service");
+ goto failed;
+ }
+
+ conn_id = dev->conn_id;
+
+ hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+ ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
+ if (!ch) {
+ error("gatt: Read descr. could not find characteristic");
+ goto failed;
+ }
+
+ hal_gatt_id_to_element_id(&cmd->descr_id, &match_id);
+ descr = queue_find(ch->descriptors, match_descr_by_element_id,
+ &match_id);
+ if (!descr) {
+ error("gatt: Read descr. could not find descriptor");
+ goto failed;
+ }
+
+ cb_data = new0(struct read_desc_data, 1);
+ if (!cb_data) {
+ error("gatt: Read descr. could not allocate callback data");
+
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data->service = srvc;
+ cb_data->conn_id = conn_id;
+ cb_data->descriptor = descr;
+ cb_data->characteristic = ch;
+
+ /* TODO: handle cmd->auth_req flag */
+ if (gatt_read_char(dev->attrib, descr->handle, read_desc_cb, cb_data)) {
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ free(cb_data);
+
+failed:
+ send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id, descr,
+ srvc, ch);
+ status = HAL_STATUS_SUCCESS;
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
}
static void handle_client_write_descriptor(const void *buf, uint16_t len)
--
1.9.0
From: Grzegorz Kolodziejczyk <[email protected]>
This adds read characteristic client command handling.
---
android/gatt.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 138 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 8dacd86..15a61d4 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -243,6 +243,26 @@ static bool match_notification(const void *a, const void *b)
return true;
}
+static bool match_char_by_element_id(const void *data, const void *user_data)
+{
+ const struct element_id *exp_id = user_data;
+ const struct characteristic *chars = data;
+ bt_uuid_t uuid;
+
+ bt_string_to_uuid(&uuid, chars->ch.uuid);
+ if (exp_id->instance == chars->id.instance)
+ return !bt_uuid_cmp(&uuid, &exp_id->uuid);
+
+ return false;
+}
+
+static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from,
+ struct element_id *to)
+{
+ to->instance = from->inst_id;
+ android2uuid(from->uuid, &to->uuid);
+}
+
static void destroy_notification(void *data)
{
struct notification_data *notification = data;
@@ -1359,13 +1379,129 @@ static void handle_client_get_descriptor(const void *buf, uint16_t len)
HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_FAILED);
}
+struct read_char_data {
+ int32_t conn_id;
+ struct service *service;
+ struct characteristic *characteristic;
+ uint8_t is_primary;
+};
+
+static void send_client_read_char_notify(int32_t status, const uint8_t *pdu,
+ uint16_t len, int32_t conn_id,
+ struct service *service,
+ struct characteristic *ch,
+ uint8_t is_primary)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf;
+ bt_uuid_t uuid;
+ ssize_t vlen;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->conn_id = conn_id;
+ ev->status = status;
+
+ ev->data.srvc_id.inst_id = service->id.instance;
+ bt_string_to_uuid(&uuid, service->primary.uuid);
+ uuid2android(&uuid, ev->data.srvc_id.uuid);
+ ev->data.srvc_id.is_primary = is_primary;
+
+ ev->data.char_id.inst_id = ch->id.instance;
+ bt_string_to_uuid(&uuid, ch->ch.uuid);
+ uuid2android(&uuid, ev->data.char_id.uuid);
+
+ ev->data.status = status;
+
+ if (pdu) {
+ vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf));
+ if (vlen < 0) {
+ error("gatt: Protocol error");
+ ev->status = GATT_FAILURE;
+ } else {
+ ev->data.len = vlen;
+ }
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC,
+ sizeof(*ev) + ev->data.len, ev);
+}
+
+static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct read_char_data *data = user_data;
+
+ send_client_read_char_notify(status, pdu, len, data->conn_id,
+ data->service, data->characteristic,
+ data->is_primary);
+
+ free(data);
+}
+
static void handle_client_read_characteristic(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
+ struct read_char_data *cb_data;
+ struct characteristic *ch;
+ struct element_id match_id;
+ struct gatt_device *dev;
+ struct service *srvc;
+ uint8_t status;
+
DBG("");
+ /* TODO authorization needs to be handled */
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* search characteristics by element id */
+ hal_gatt_id_to_element_id(&cmd->gatt_id, &match_id);
+ ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
+ if (!ch) {
+ error("gatt: Characteristic with inst_id: %d not found",
+ cmd->gatt_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data = new0(struct read_char_data, 1);
+ if (!cb_data) {
+ error("gatt: Cannot allocate cb data");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data->service = srvc;
+ cb_data->conn_id = dev->conn_id;
+ cb_data->characteristic = ch;
+ cb_data->is_primary = cmd->srvc_id.is_primary;
+
+ if (!gatt_read_char(dev->attrib, ch->ch.value_handle,
+ read_char_cb, cb_data)) {
+ /* Read characteristic failed so we need to send
+ * notification with failed status as Android waits
+ * for it. Even though we reply success as a reply for
+ * this command.
+ */
+ error("gatt: Cannot read characteristic with inst_id: %d",
+ cmd->gatt_id.inst_id);
+ send_client_read_char_notify(GATT_FAILURE, NULL, 0,
+ dev->conn_id, srvc, ch,
+ cmd->srvc_id.is_primary);
+ free(cb_data);
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
- HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
}
static void handle_client_write_characteristic(const void *buf, uint16_t len)
--
1.9.0
This patch introduces GATT_SUCCESS and GATT_FAILURE status codes
according to Android API. This patch also fixes event status codes.
---
android/gatt.c | 41 ++++++++++++++++++++++++-----------------
1 file changed, 24 insertions(+), 17 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index e361506..8dacd86 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -48,6 +48,9 @@
#include "attrib/gatt.h"
#include "btio/btio.h"
+#define GATT_SUCCESS 0x00000000
+#define GATT_FAILURE 0x00000101
+
struct gatt_client {
int32_t id;
uint8_t uuid[16];
@@ -343,7 +346,7 @@ static void handle_client_register(const void *buf, uint16_t len)
status = HAL_STATUS_SUCCESS;
- ev.status = status;
+ ev.status = GATT_SUCCESS;
ev.client_if = client->id;
memcpy(ev.app_uuid, client->uuid, sizeof(client->uuid));
@@ -448,7 +451,7 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
sizeof(ev_res), &ev_res);
}
- ev.status = HAL_STATUS_SUCCESS;
+ ev.status = GATT_SUCCESS;
done:
ev.conn_id = dev->conn_id;
@@ -496,7 +499,7 @@ static void client_disconnect_notify(void *data, void *user_data)
struct gatt_device *dev = user_data;
int32_t id = PTR_TO_INT(data);
- send_client_disconnect_notify(id, dev, HAL_STATUS_SUCCESS);
+ send_client_disconnect_notify(id, dev, GATT_SUCCESS);
}
static bool is_device_wating_for_connect(const bdaddr_t *addr,
@@ -612,7 +615,7 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
struct hal_ev_gatt_client_connect ev;
GAttrib *attrib;
static uint32_t conn_id = 0;
- uint8_t status;
+ int32_t status;
/* Take device from conn waiting queue */
dev = queue_remove_if(conn_wait_queue, match_dev_by_bdaddr, addr);
@@ -630,14 +633,14 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
if (gerr) {
error("gatt: connection failed %s", gerr->message);
- status = HAL_STATUS_FAILED;
+ status = GATT_FAILURE;
goto reply;
}
attrib = g_attrib_new(io);
if (!attrib) {
error("gatt: unable to create new GAttrib instance");
- status = HAL_STATUS_FAILED;
+ status = GATT_FAILURE;
goto reply;
}
@@ -650,11 +653,11 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
if (!queue_push_tail(conn_list, dev)) {
error("gatt: Cannot push dev on conn_list");
connection_cleanup(dev);
- status = HAL_STATUS_FAILED;
+ status = GATT_FAILURE;
goto reply;
}
- status = HAL_STATUS_SUCCESS;
+ status = GATT_SUCCESS;
goto reply;
reply:
@@ -932,7 +935,7 @@ reply:
struct hal_ev_gatt_client_connect ev;
ev.conn_id = dev->conn_id;
- ev.status = HAL_STATUS_SUCCESS;
+ ev.status = GATT_SUCCESS;
ev.client_if = cmd->client_if;
bdaddr2android(&addr, &ev.bda);
@@ -982,7 +985,7 @@ reply:
* If this is last client, this is still OK to do because on connect
* request we do le scan and wait until remote device start
* advertisement */
- send_client_disconnect_notify(cmd->client_if, dev, HAL_STATUS_SUCCESS);
+ send_client_disconnect_notify(cmd->client_if, dev, GATT_SUCCESS);
/* If there is more clients just return */
if (!queue_isempty(dev->clients))
@@ -1087,7 +1090,7 @@ static void get_included_cb(uint8_t status, GSList *included, void *user_data)
bt_uuid_t included_uuid;
ev.conn_id = device->conn_id;
- ev.status = HAL_STATUS_SUCCESS;
+ ev.status = GATT_SUCCESS;
ev.srvc_id.inst_id = 0;
uuid2android(&uuid, ev.srvc_id.uuid);
@@ -1103,7 +1106,7 @@ static void get_included_cb(uint8_t status, GSList *included, void *user_data)
/* Android expects notification with error status in the end */
ev.conn_id = device->conn_id;
- ev.status = HAL_STATUS_FAILED;
+ ev.status = GATT_FAILURE;
ev.srvc_id.inst_id = 0;
uuid2android(&uuid, ev.srvc_id.uuid);
@@ -1176,7 +1179,7 @@ static void send_client_char_notify(const struct characteristic *ch,
struct hal_ev_gatt_client_get_characteristic ev;
memset(&ev, 0, sizeof(ev));
- ev.status = ch ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+ ev.status = ch ? GATT_SUCCESS : GATT_FAILURE;
if (ch) {
ev.char_prop = ch->ch.properties;
@@ -1453,6 +1456,7 @@ static void handle_client_register_for_notification(const void *buf,
struct service *service;
int32_t conn_id = 0;
uint8_t status;
+ int32_t gatt_status;
bdaddr_t addr;
DBG("");
@@ -1547,8 +1551,9 @@ static void handle_client_register_for_notification(const void *buf,
status = HAL_STATUS_SUCCESS;
failed:
- send_register_for_notification_ev(conn_id, 1, status, &cmd->srvc_id,
- &cmd->char_id);
+ gatt_status = status ? GATT_SUCCESS : GATT_FAILURE;
+ send_register_for_notification_ev(conn_id, 1, gatt_status,
+ &cmd->srvc_id, &cmd->char_id);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
status);
@@ -1563,6 +1568,7 @@ static void handle_client_deregister_for_notification(const void *buf,
struct gatt_device *dev;
int32_t conn_id = 0;
uint8_t status;
+ int32_t gatt_status;
bdaddr_t addr;
DBG("");
@@ -1598,8 +1604,9 @@ static void handle_client_deregister_for_notification(const void *buf,
status = HAL_STATUS_SUCCESS;
failed:
- send_register_for_notification_ev(conn_id, 0, status, &cmd->srvc_id,
- &cmd->char_id);
+ gatt_status = status ? GATT_SUCCESS : GATT_FAILURE;
+ send_register_for_notification_ev(conn_id, 0, gatt_status,
+ &cmd->srvc_id, &cmd->char_id);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
--
1.9.0
We should match uuid from element_id struct to avoid uuid creation every
time match function is being called.
---
android/gatt.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index f5e44be..b8c52c0 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -220,11 +220,9 @@ static bool match_srvc_by_element_id(const void *data, const void *user_data)
{
const struct element_id *exp_id = user_data;
const struct service *service = data;
- bt_uuid_t uuid;
- bt_string_to_uuid(&uuid, service->primary.uuid);
if (service->id.instance == exp_id->instance)
- return !bt_uuid_cmp(&uuid, &exp_id->uuid);
+ return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid);
return false;
}
@@ -289,11 +287,9 @@ static bool match_char_by_element_id(const void *data, const void *user_data)
{
const struct element_id *exp_id = user_data;
const struct characteristic *chars = data;
- bt_uuid_t uuid;
- bt_string_to_uuid(&uuid, chars->ch.uuid);
if (exp_id->instance == chars->id.instance)
- return !bt_uuid_cmp(&uuid, &exp_id->uuid);
+ return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid);
return false;
}
--
1.9.0
---
android/gatt.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 128 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 0a4159e..f5e44be 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -2003,12 +2003,139 @@ done:
HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
}
+struct write_descr_data {
+ int32_t conn_id;
+ struct service *service;
+ struct characteristic *characteristic;
+ struct descriptor *descriptor;
+
+ int32_t write_type;
+};
+
+static void send_client_descr_write_notify(int32_t status, int32_t conn_id,
+ const struct descriptor *descr,
+ const struct service *srvc,
+ const struct characteristic *ch) {
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf;
+
+ memset(buf, 0, sizeof(buf));
+
+ ev->status = status;
+ ev->conn_id = conn_id;
+
+ if (srvc) {
+ ev->data.srvc_id.inst_id = srvc->id.instance;
+ uuid2android(&srvc->id.uuid, ev->data.srvc_id.uuid);
+ }
+
+ if (ch) {
+ ev->data.char_id.inst_id = ch->id.instance;
+ uuid2android(&ch->id.uuid, ev->data.char_id.uuid);
+ }
+
+ if (descr) {
+ ev->data.descr_id.inst_id = descr->id.instance;
+ uuid2android(&descr->id.uuid, ev->data.descr_id.uuid);
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR,
+ sizeof(*ev), ev);
+}
+
+
+static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct read_desc_data *cb_data = user_data;
+
+ if (status != 0)
+ error("gatt: Write descriptors failed: %s\n",
+ att_ecode2str(status));
+
+ send_client_descr_write_notify(status, cb_data->conn_id,
+ cb_data->descriptor, cb_data->service,
+ cb_data->characteristic);
+
+ free(cb_data);
+
+}
+
static void handle_client_write_descriptor(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_write_descriptor *cmd = buf;
+ struct write_descr_data *cb_data;
+ struct characteristic *ch = NULL;
+ struct descriptor *descr = NULL;
+ struct service *srvc = NULL;
+ struct element_id match_id;
+ struct gatt_device *dev;
+ int32_t conn_id = 0;
+ uint8_t status;
+
DBG("");
+ if (len != sizeof(*cmd) + cmd->len * sizeof(cmd->value[0])) {
+ error("gatt: Write descr. msg size invalid (%d bytes)", len);
+
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
+ error("gatt: Read descr. could not find service");
+ goto failed;
+ }
+
+ conn_id = dev->conn_id;
+
+ hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+ ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
+ if (!ch) {
+ error("gatt: Read descr. could not find characteristic");
+ goto failed;
+ }
+
+ hal_gatt_id_to_element_id(&cmd->descr_id, &match_id);
+ descr = queue_find(ch->descriptors, match_descr_by_element_id,
+ &match_id);
+ if (!descr) {
+ error("gatt: Read descr. could not find descriptor");
+ goto failed;
+ }
+
+ cb_data = new0(struct write_descr_data, 1);
+ if (!cb_data) {
+ error("gatt: Read descr. could not allocate callback data");
+
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data->service = srvc;
+ cb_data->conn_id = conn_id;
+ cb_data->descriptor = descr;
+ cb_data->characteristic = ch;
+ cb_data->write_type = cmd->write_type;
+
+ if (gatt_write_char(dev->attrib, descr->handle, cmd->value,
+ cmd->len * sizeof(cmd->value[0]),
+ write_descr_cb, cb_data)) {
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ free(cb_data);
+
+failed:
+ send_client_descr_write_notify(GATT_FAILURE, conn_id, descr, srvc, ch);
+ status = HAL_STATUS_SUCCESS;
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status);
}
static void handle_client_execute_write(const void *buf, uint16_t len)
--
1.9.0
This patch adds searching for descriptors and sending them in
notifications. Descriptors are cached. In case of initialial descriptor
provided, next after the given is sent.
---
android/gatt.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 151 insertions(+), 9 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index fec55d4..1fe7035 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -237,6 +237,16 @@ static bool match_char_by_higher_inst_id(const void *data,
return inst_id < ch->id.instance;
}
+static bool match_descr_by_higher_inst_id(const void *data,
+ const void *user_data)
+{
+ const struct descriptor *descr = data;
+ uint8_t instance = PTR_TO_INT(user_data);
+
+ /* For now we match instance as it is unique */
+ return instance < descr->id.instance;
+}
+
static bool match_char_by_instance(const void *data, const void *user_data)
{
const struct characteristic *ch = data;
@@ -1396,6 +1406,12 @@ done:
HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
}
+struct discover_desc_data {
+ int32_t conn_id;
+ struct service *service;
+ struct characteristic *characteristic;
+};
+
static void send_client_descr_notify(int32_t status, int32_t conn_id,
const struct descriptor *descr,
const struct service *srvc,
@@ -1426,8 +1442,78 @@ static void send_client_descr_notify(int32_t status, int32_t conn_id,
}
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_GET_DESCRIPTOR,
- sizeof(ev), &ev);
+ HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
+}
+
+
+static void cache_all_descr(const uint8_t *pdu, guint16 len,
+ struct queue *queue)
+{
+ struct att_data_list *list;
+ guint8 format;
+ int i;
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (!list || !queue)
+ return;
+
+ for (i = 0; i < list->num; i++) {
+ char uuidstr[MAX_LEN_UUID_STR];
+ struct descriptor *descr;
+ bt_uuid_t uuid128;
+ uint16_t handle;
+ uint8_t *value;
+ bt_uuid_t uuid;
+
+ value = list->data[i];
+ handle = get_le16(value);
+
+ if (format == ATT_FIND_INFO_RESP_FMT_16BIT) {
+ bt_uuid16_create(&uuid, get_le16(&value[2]));
+ bt_uuid_to_uuid128(&uuid, &uuid128);
+ } else {
+ uint128_t u128;
+
+ bswap_128(&value[2], &u128);
+ bt_uuid128_create(&uuid128, u128);
+ }
+
+ bt_uuid_to_string(&uuid128, uuidstr, MAX_LEN_UUID_STR);
+ DBG("gatt: Cached descriptor handle = 0x%04x, uuid = %s\n",
+ handle, uuidstr);
+
+ descr = new0(struct descriptor, 1);
+ if (!descr)
+ break;
+
+ descr->id.instance = i;
+ descr->handle = handle;
+ descr->id.uuid = uuid128;
+
+ if (!queue_push_tail(queue, descr))
+ free(descr);
+ }
+
+ att_data_list_free(list);
+}
+
+static void gatt_discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct discover_desc_data *data = user_data;
+ struct descriptor *descr = NULL;
+
+ if (status != 0) {
+ error("gatt: Discover all char descriptors failed: %s\n",
+ att_ecode2str(status));
+ } else if (data->characteristic) {
+ cache_all_descr(pdu, len, data->characteristic->descriptors);
+ descr = queue_peek_head(data->characteristic->descriptors);
+ }
+
+ send_client_descr_notify(status, data->conn_id, descr, data->service,
+ data->characteristic);
+ free(data);
}
static void handle_client_get_descriptor(const void *buf, uint16_t len)
@@ -1435,9 +1521,11 @@ static void handle_client_get_descriptor(const void *buf, uint16_t len)
const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
struct characteristic *ch = NULL;
struct descriptor *descr = NULL;
+ struct characteristic *next_ch = NULL;
struct service *srvc = NULL;
struct element_id match_id;
struct gatt_device *dev;
+ uint16_t start, end;
int32_t conn_id = 0;
uint8_t status;
@@ -1453,8 +1541,8 @@ static void handle_client_get_descriptor(const void *buf, uint16_t len)
hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
- status = HAL_STATUS_FAILED;
- goto done;
+ error("gatt: cound not find service");
+ goto failed;
}
conn_id = dev->conn_id;
@@ -1462,18 +1550,72 @@ static void handle_client_get_descriptor(const void *buf, uint16_t len)
hal_gatt_id_to_element_id(&cmd->gatt_id[0], &match_id);
ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
if (!ch) {
- status = HAL_STATUS_FAILED;
- goto done;
+ error("gatt: cound not find characteristic");
+ goto failed;
}
+ /* Clip range to given characteristic */
+ start = ch->ch.value_handle + 1;
+ end = srvc->primary.range.end;
+
if (queue_isempty(ch->descriptors)) {
- /* TODO: Cache descriptors */
+ /* Use next characteristic start as end. If there is none -
+ * service end is valid end.
+ * TODO: we should cache char end handle to avoid this search */
+ next_ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+ INT_TO_PTR(match_id.instance));
+ if (next_ch)
+ end = next_ch->ch.handle - 1;
+
+ /* If characteristic is not empty create descriptor cache, else
+ * notify with bad status. */
+ if (start <= end) {
+ struct discover_desc_data *cb_data;
+
+ cb_data = new0(struct discover_desc_data, 1);
+ if (!cb_data) {
+ error("gatt: cb_data allocation error");
+
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data->service = srvc;
+ cb_data->conn_id = conn_id;
+ cb_data->characteristic = ch;
+
+ if (gatt_discover_char_desc(dev->attrib, start, end,
+ gatt_discover_desc_cb,
+ cb_data)) {
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+ free(cb_data);
+ }
+
+ goto failed;
}
- /* TODO: Send from cache */
- send_client_descr_notify(GATT_FAILURE, conn_id, descr, srvc, ch);
+ /* Send from cache */
+ if (cmd->number > 1)
+ descr = queue_find(ch->descriptors,
+ match_descr_by_higher_inst_id,
+ INT_TO_PTR(cmd->gatt_id[1].inst_id));
+ else
+ descr = queue_peek_head(ch->descriptors);
+ if (descr) {
+ send_client_descr_notify(GATT_SUCCESS, conn_id, descr, srvc,
+ ch);
+ status = HAL_STATUS_SUCCESS;
+ goto done;
+ }
+
+failed:
+ send_client_descr_notify(GATT_FAILURE, conn_id, descr, srvc, ch);
status = HAL_STATUS_SUCCESS;
+
done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
--
1.9.0
From: Grzegorz Kolodziejczyk <[email protected]>
This adds write characteristic client command handling.
---
android/gatt.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 108 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 15a61d4..4dd16e7 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -1504,13 +1504,119 @@ done:
HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
}
+struct write_char_data {
+ int32_t conn_id;
+ struct service *service;
+ struct characteristic *characteristic;
+ uint8_t is_primary;
+};
+
+static void send_client_write_char_notify(int32_t status, int32_t conn_id,
+ struct service *service,
+ struct characteristic *characteristic,
+ uint8_t is_primary)
+{
+ struct hal_ev_gatt_client_write_characteristic ev;
+ bt_uuid_t uuid;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.conn_id = conn_id;
+ ev.status = status;
+
+ ev.data.srvc_id.inst_id = service->id.instance;
+ bt_string_to_uuid(&uuid, service->primary.uuid);
+ memcpy(&ev.data.srvc_id.uuid, &uuid.value.u128.data,
+ sizeof(ev.data.srvc_id.uuid));
+ ev.data.srvc_id.is_primary = is_primary;
+
+ ev.data.char_id.inst_id = characteristic->id.instance;
+ bt_string_to_uuid(&uuid, characteristic->ch.uuid);
+ memcpy(&ev.data.char_id.uuid, &uuid.value.u128.data,
+ sizeof(ev.data.char_id.uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC,
+ sizeof(ev), &ev);
+}
+
+static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct write_char_data *data = user_data;
+
+ send_client_write_char_notify(status, data->conn_id, data->service,
+ data->characteristic, data->is_primary);
+
+ free(data);
+}
+
static void handle_client_write_characteristic(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
+ struct write_char_data *cb_data;
+ struct characteristic *ch;
+ struct element_id match_id;
+ struct gatt_device *dev;
+ struct service *srvc;
+ uint8_t status;
+ uint8_t value[cmd->len];
+
DBG("");
+ if (len != sizeof(*cmd) + cmd->len) {
+ error("Invalid write characteristic size (%u bytes), "
+ "terminating", len);
+ raise(SIGTERM);
+ return;
+ }
+
+ memcpy(value, cmd->value, cmd->len);
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ /* search characteristics by instance id */
+ hal_gatt_id_to_element_id(&cmd->gatt_id, &match_id);
+
+ ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
+ if (!ch) {
+ error("gatt: Characteristic with inst_id: %d not found",
+ cmd->gatt_id.inst_id);
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data = new0(struct write_char_data, 1);
+ if (!cb_data) {
+ error("gatt: Cannot allocate call data");
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ cb_data->service = srvc;
+ cb_data->characteristic = ch;
+ cb_data->conn_id = dev->conn_id;
+ cb_data->is_primary = cmd->srvc_id.is_primary;
+
+ if (!gatt_write_char(dev->attrib, ch->ch.value_handle, value,
+ sizeof(value), write_char_cb, cb_data))
+ {
+ error("gatt: Cannot read characteristic with inst_id: %d",
+ cmd->gatt_id.inst_id);
+ send_client_write_char_notify(GATT_FAILURE, dev->conn_id, srvc,
+ ch, cmd->srvc_id.is_primary);
+ free(cb_data);
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
- HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
}
static void handle_client_read_descriptor(const void *buf, uint16_t len)
--
1.9.0
This adds basic get characteristic descriptor command handling.
In case of no descriptor proper notification with bad status is being
sent.
---
android/gatt.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 77 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index be38f66..fec55d4 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -31,6 +31,7 @@
#include <glib.h>
#include <errno.h>
#include <sys/socket.h>
+#include <assert.h>
#include "ipc.h"
#include "ipc-common.h"
@@ -1395,12 +1396,87 @@ done:
HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
}
+static void send_client_descr_notify(int32_t status, int32_t conn_id,
+ const struct descriptor *descr,
+ const struct service *srvc,
+ const struct characteristic *ch)
+{
+ struct hal_ev_gatt_client_get_descriptor ev;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.status = status;
+ ev.conn_id = conn_id;
+
+ if (descr) {
+ ev.descr_id.inst_id = descr->id.instance;
+ uuid2android(&descr->id.uuid, ev.descr_id.uuid);
+ }
+
+ if (srvc) {
+ /* TODO: handle included services */
+ ev.srvc_id.is_primary = 1;
+ ev.srvc_id.inst_id = srvc->id.instance;
+ uuid2android(&srvc->id.uuid, ev.srvc_id.uuid);
+ }
+
+ if (ch) {
+ ev.char_id.inst_id = ch->id.instance;
+ uuid2android(&ch->id.uuid, ev.char_id.uuid);
+ }
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_GET_DESCRIPTOR,
+ sizeof(ev), &ev);
+}
+
static void handle_client_get_descriptor(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
+ struct characteristic *ch = NULL;
+ struct descriptor *descr = NULL;
+ struct service *srvc = NULL;
+ struct element_id match_id;
+ struct gatt_device *dev;
+ int32_t conn_id = 0;
+ uint8_t status;
+
DBG("");
+ assert(cmd->number);
+
+ if (len != sizeof(*cmd) + cmd->number * sizeof(cmd->gatt_id[0])) {
+ error("gatt: Get descr. bad cmd size (%u b), terminating", len);
+ raise(SIGTERM);
+ return;
+ }
+
+ hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+ if (!find_service(cmd->conn_id, &match_id, &dev, &srvc)) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ conn_id = dev->conn_id;
+
+ hal_gatt_id_to_element_id(&cmd->gatt_id[0], &match_id);
+ ch = queue_find(srvc->chars, match_char_by_element_id, &match_id);
+ if (!ch) {
+ status = HAL_STATUS_FAILED;
+ goto done;
+ }
+
+ if (queue_isempty(ch->descriptors)) {
+ /* TODO: Cache descriptors */
+ }
+
+ /* TODO: Send from cache */
+ send_client_descr_notify(GATT_FAILURE, conn_id, descr, srvc, ch);
+
+ status = HAL_STATUS_SUCCESS;
+done:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
}
struct read_char_data {
--
1.9.0