2014-04-29 16:46:49

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 00/40] android/gatt: GATT server implementation

This patch set adds major part of GATT server support for Android:
* GATT database extension to get, read and write data
* Handling GATT Server Android requests
* Create listening socket for BLE.
* Support GATT Service
* Support Device Information Service
* Support Service Changed (skeleton)
* some bugfixes

Still some improvements are needed.

Tested on PC and Nexus 5 as peripheral device.

v2:
* Handled comments from Johan
* Added complete callback to database requests
* some bugfixes found during tests with haltest
* Rebase on upstream

Grzegorz Kolodziejczyk (3):
android/gatt: Add support for ATT read by type
android/gatt: Add MTU request cmd handling
android/gatt: Add Find info gatt server cmd handling

Jakub Tyszkowski (3):
android/gatt: Remove redundant find function parameter
android/gatt: Register device information service
android/gatt: Register GATT service

Lukasz Rymanowski (24):
android/gatt: Rename listen_clients to listen_apps
android/gatt: Add listening socket for GATT
android/gatt: Assume that each server wants waits for connection
android/gatt: Add ATT msg handler
shared: Use pointer for request data instead of int
shared/gatt: Extend read callback with offset
shared/gatt: Add complete command callback
android/bluetooth: Add function for getting device Android name
android/gatt: Add register GAP Service
gatt: Add some characteristics uuids
shared: Extend write callback with offset
android/gatt: Add support for ATT read by group type
shared/gatt: Add support to read from database
android/gatt: Move struct req_data up in the file
android/gatt: Add support to read request
shared/gatt: Add support for write request
android/gatt: Add support for write request
android/gatt: Add support for execute write
android/gatt: Move struct req_data upper in the file
android/gatt: Add write_cb to GATT server
android/gatt: Add read_cb for GATT Server
android/hal-gatt-api: Fix IPC definition for send response
android/gatt: Add support for GATT server send response
android/gatt: Add support for send indication

Marcin Kraglak (10):
android/gatt: Add service functionality
android/gatt: Add implementation of delete service
android/gatt: Add included service implementation
android/gatt: Add characteristic implementation
android/gatt: Add handling of start service command
android/gatt: Add stop service command handling
android/gatt: Add descriptor implementation
shared/gatt: Add function to read by group type
shared/gatt: Add function to find by type
shared/gatt: Add function to read by type

android/bluetooth.c | 5 +
android/bluetooth.h | 1 +
android/gatt.c | 1254 +++++++++++++++++++++++++++++++++++++++++++++++---
android/hal-gatt.c | 7 +-
android/hal-msg.h | 3 +
lib/uuid.h | 7 +
src/shared/gatt-db.c | 321 +++++++++++++
src/shared/gatt-db.h | 69 ++-
8 files changed, 1588 insertions(+), 79 deletions(-)

--
1.8.4



2014-04-30 09:16:47

by Lukasz Rymanowski

[permalink] [raw]
Subject: Re: [PATCH v2 29/40] android/gatt: Add support to read request

Hi Johan,

On Tue, Apr 29, 2014 at 8:20 PM, Johan Hedberg <[email protected]> wrote:
> Hi Lukasz,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> +static void gatt_request_complete(void *data, const uint8_t *pdu, uint16_t len)
>> +{
>> + struct req_data *d = data;
>> +
>> + if (pdu)
>> + g_attrib_send(d->dev->attrib, 0, pdu, len, NULL, NULL, NULL);
>> + else
>> + error("gatt: gatt_request_complete: pdu not present ?!");
>> +
>> + free(d);
>> +}
>> +
>> +static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
>> + struct gatt_device *dev)
>> +{
>> + uint16_t handle;
>> + uint16_t len;
>> + uint16_t offset = 0;
>> + struct req_data *req_data;
>> +
>> + DBG("");
>> +
>> + req_data = new0(struct req_data, 1);
>> + if (!req_data)
>> + return ATT_ECODE_INSUFF_RESOURCES;
>> +
>> + switch (cmd[0]) {
>> + case ATT_OP_READ_BLOB_REQ:
>> + len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
>> + if (!len) {
>> + free(req_data);
>> + return ATT_ECODE_INVALID_PDU;
>> + }
>> + req_data->long_read = true;
>> + break;
>> + case ATT_OP_READ_REQ:
>> + len = dec_read_req(cmd, cmd_len, &handle);
>> + if (!len) {
>> + free(req_data);
>> + return ATT_ECODE_INVALID_PDU;
>> + }
>> + break;
>> + default:
>> + error("gatt: Unexpected read type 0x%02x", cmd[0]);
>> + free(req_data);
>> + return ATT_ECODE_REQ_NOT_SUPP;
>> + }
>> +
>> + req_data->dev = dev;
>> + req_data->opcode = cmd[0];
>> +
>> + if (!gatt_db_read(gatt_db, handle, offset, req_data, gatt_request_complete)) {
>> + free(req_data);
>> + return ATT_ECODE_UNLIKELY;
>> + }
>
> I don't understand the need to have so many levels of callback passing
> instead of having simple direct calls, especially since all functions
> are in the same c-file anyway. I.e. why can't the characteristic
> read/write callbacks be responsible themselves for sending the response
> PDU (if that's all your gatt_request_complete function is needed for).
>
> This passing a callback (gatt_request_complete in this case) as a
> parameter to another callback (the read/write callback) doesn't really
> help in keeping things simple and understandable.
>

in v4 it should me simpler as I present you on IRC.

> Johan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

Lukasz

2014-04-29 18:20:25

by Johan Hedberg

[permalink] [raw]
Subject: Re: [PATCH v2 29/40] android/gatt: Add support to read request

Hi Lukasz,

On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +static void gatt_request_complete(void *data, const uint8_t *pdu, uint16_t len)
> +{
> + struct req_data *d = data;
> +
> + if (pdu)
> + g_attrib_send(d->dev->attrib, 0, pdu, len, NULL, NULL, NULL);
> + else
> + error("gatt: gatt_request_complete: pdu not present ?!");
> +
> + free(d);
> +}
> +
> +static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
> + struct gatt_device *dev)
> +{
> + uint16_t handle;
> + uint16_t len;
> + uint16_t offset = 0;
> + struct req_data *req_data;
> +
> + DBG("");
> +
> + req_data = new0(struct req_data, 1);
> + if (!req_data)
> + return ATT_ECODE_INSUFF_RESOURCES;
> +
> + switch (cmd[0]) {
> + case ATT_OP_READ_BLOB_REQ:
> + len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
> + if (!len) {
> + free(req_data);
> + return ATT_ECODE_INVALID_PDU;
> + }
> + req_data->long_read = true;
> + break;
> + case ATT_OP_READ_REQ:
> + len = dec_read_req(cmd, cmd_len, &handle);
> + if (!len) {
> + free(req_data);
> + return ATT_ECODE_INVALID_PDU;
> + }
> + break;
> + default:
> + error("gatt: Unexpected read type 0x%02x", cmd[0]);
> + free(req_data);
> + return ATT_ECODE_REQ_NOT_SUPP;
> + }
> +
> + req_data->dev = dev;
> + req_data->opcode = cmd[0];
> +
> + if (!gatt_db_read(gatt_db, handle, offset, req_data, gatt_request_complete)) {
> + free(req_data);
> + return ATT_ECODE_UNLIKELY;
> + }

I don't understand the need to have so many levels of callback passing
instead of having simple direct calls, especially since all functions
are in the same c-file anyway. I.e. why can't the characteristic
read/write callbacks be responsible themselves for sending the response
PDU (if that's all your gatt_request_complete function is needed for).

This passing a callback (gatt_request_complete in this case) as a
parameter to another callback (the read/write callback) doesn't really
help in keeping things simple and understandable.

Johan

2014-04-29 16:47:28

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 39/40] android/gatt: Add support for GATT server send response

---
android/gatt.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 03ff1f3..80880eb 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3305,12 +3305,40 @@ static void send_gatt_response(uint8_t opcode, uint16_t handle,
uint16_t length;
uint8_t pdu[ATT_DEFAULT_LE_MTU];

+ DBG("");
+
if (!status) {
length = enc_error_resp(opcode, handle, status, pdu,
sizeof(pdu));
- cb(req_data, pdu, length);
+ goto done;
}
- /* TODO: Send responses for other commands */
+
+ switch (opcode) {
+ case ATT_OP_EXEC_WRITE_REQ:
+ length = enc_exec_write_resp(pdu);
+ break;
+ case ATT_OP_WRITE_REQ:
+ length = enc_write_resp(pdu);
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ length = enc_prep_write_resp(handle, offset, data, len, pdu,
+ sizeof(pdu));
+ break;
+ case ATT_OP_READ_BLOB_REQ:
+ length = enc_read_blob_resp((uint8_t *)data, len, offset, pdu,
+ sizeof(pdu));
+ break;
+ case ATT_OP_READ_REQ:
+ length = enc_read_resp((uint8_t *)data, len, pdu, sizeof(pdu));
+ break;
+ default:
+ error("gatt: Unexpected opcode");
+ cb(req_data, NULL, 0);
+ return;
+ }
+
+done:
+ cb(req_data, pdu, length);
}

static void read_cb(uint16_t handle, uint16_t offset, void *req_data,
@@ -3643,10 +3671,44 @@ static void handle_server_send_indication(const void *buf, uint16_t len)

static void handle_server_send_response(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_server_send_response *cmd = buf;
+ struct app_connection *conn;
+ struct gatt_app *app;
+ uint8_t status;
+
DBG("");

+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ error("gatt: cound not found connection");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ app = conn->app;
+
+ if (cmd->trans_id != app->pend_trans_id) {
+ error("gatt: trans_id != pend_trans_id (%d!=%d)",
+ cmd->trans_id, app->pend_trans_id);
+
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ send_gatt_response(conn->app->data->opcode, cmd->handle, cmd->offset,
+ cmd->status, cmd->len, cmd->data,
+ app->data, app->gatt_db_complete_cb);
+
+ /* Clean request data */
+ conn->app->data = NULL;
+ conn->app->pend_trans_id = 0;
+ conn->app->gatt_db_complete_cb = NULL;
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_SERVER_SEND_RESPONSE, HAL_STATUS_FAILED);
+ HAL_OP_GATT_SERVER_SEND_RESPONSE, status);
}

static const struct ipc_handler cmd_handlers[] = {
--
1.8.4


2014-04-29 16:47:29

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 40/40] android/gatt: Add support for send indication

With this patch Android app can send indication to remote device
---
android/gatt.c | 33 ++++++++++++++++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index 80880eb..3638ae8 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3663,10 +3663,41 @@ failed:

static void handle_server_send_indication(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_server_send_indication *cmd = buf;
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct app_connection *conn;
+ struct gatt_app *app;
+ uint8_t status;
+ uint16_t length;
+
DBG("");

+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ error("gatt: Could not find connection");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ app = conn->app;
+
+ if (cmd->confirm)
+ /* TODO: Add data to track confirmation for this request */
+ length = enc_indication(cmd->attribute_handle,
+ (uint8_t *)cmd->value, cmd->len,
+ pdu, sizeof(pdu));
+ else
+ length = enc_notification(cmd->attribute_handle,
+ (uint8_t *)cmd->value, cmd->len,
+ pdu, sizeof(pdu));
+
+ app->gatt_db_complete_cb(app->data, pdu, length);
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_SERVER_SEND_INDICATION, HAL_STATUS_FAILED);
+ HAL_OP_GATT_SERVER_SEND_INDICATION, status);
}

static void handle_server_send_response(const void *buf, uint16_t len)
--
1.8.4


2014-04-29 16:47:27

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 38/40] android/hal-gatt-api: Fix IPC definition for send response

---
android/hal-gatt.c | 7 +++++--
android/hal-msg.h | 3 +++
2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/android/hal-gatt.c b/android/hal-gatt.c
index 16b5607..e1faccb 100644
--- a/android/hal-gatt.c
+++ b/android/hal-gatt.c
@@ -1237,9 +1237,12 @@ static bt_status_t send_response(int conn_id, int trans_id, int status,
cmd->conn_id = conn_id;
cmd->trans_id = trans_id;
cmd->status = status;
- cmd->len = sizeof(*response);
+ cmd->handle = response->attr_value.handle;
+ cmd->offset = response->attr_value.offset;
+ cmd->auth_req = response->attr_value.auth_req;
+ cmd->len = response->attr_value.len;

- memcpy(cmd->data, response, sizeof(*response));
+ memcpy(cmd->data, response->attr_value.value, cmd->len);

return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_SEND_RESPONSE,
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 9d28866..09bd9a0 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -847,6 +847,9 @@ struct hal_cmd_gatt_server_send_indication {
struct hal_cmd_gatt_server_send_response {
int32_t conn_id;
int32_t trans_id;
+ uint16_t handle;
+ uint16_t offset;
+ uint8_t auth_req;
int32_t status;
uint16_t len;
uint8_t data[0];
--
1.8.4


2014-04-29 16:47:26

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 37/40] android/gatt: Add read_cb for GATT Server

This patch attach read callback to descriptors and characteristics
registered by Android app.
---
android/gatt.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index dfd0690..03ff1f3 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3313,6 +3313,55 @@ static void send_gatt_response(uint8_t opcode, uint16_t handle,
/* TODO: Send responses for other commands */
}

+static void read_cb(uint16_t handle, uint16_t offset, void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data)
+{
+ struct hal_ev_gatt_server_request_read ev;
+ struct req_data *data = req_data;
+ struct gatt_device *dev = data->dev;
+ struct gatt_app *app;
+ struct app_connection *conn;
+ int32_t id = PTR_TO_INT(user_data);
+ static int32_t trans_id = 1;
+
+ app = find_app_by_id(id);
+ if (!app) {
+ error("gatt: read_cb, cound not found app id");
+ goto failed;
+ }
+
+ conn = find_conn(&dev->bdaddr, app->id);
+ if (!conn) {
+ error("gatt: read_cb, cound not found connection");
+ goto failed;
+ }
+
+ memset(&ev, 0, sizeof(ev));
+
+ /* Store the request data, complete callback and transaction id */
+ app->data = req_data;
+ app->gatt_db_complete_cb = cb;
+ app->pend_trans_id = trans_id++;
+
+ bdaddr2android(&dev->bdaddr, ev.bdaddr);
+ ev.conn_id = conn->id;
+ ev.attr_handle = handle;
+ ev.offset = offset;
+ ev.is_long = data->long_read;
+ ev.trans_id = app->pend_trans_id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_READ,
+ sizeof(ev), &ev);
+
+ return;
+
+failed:
+ send_gatt_response(data->opcode, handle, 0, ATT_ECODE_UNLIKELY, 0,
+ NULL, data, cb);
+}
+
static void write_cb(uint16_t handle, uint16_t offset,
const uint8_t *value,
size_t len, void *req_data,
@@ -3409,7 +3458,7 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len)
cmd->service_handle,
&uuid, cmd->permissions,
cmd->properties,
- NULL, write_cb,
+ read_cb, write_cb,
INT_TO_PTR(app_id));
if (!ev.char_handle)
status = HAL_STATUS_FAILED;
@@ -3455,7 +3504,7 @@ static void handle_server_add_descriptor(const void *buf, uint16_t len)
ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
cmd->service_handle,
&uuid, cmd->permissions,
- NULL, write_cb,
+ read_cb, write_cb,
INT_TO_PTR(app_id));
if (!ev.descr_handle)
status = HAL_STATUS_FAILED;
--
1.8.4


2014-04-29 16:47:25

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 36/40] android/gatt: Add write_cb to GATT server

Write callback is now attached to characteristics and descriptors
registered by Android app.

Transaction id is generated and stored in gatt_app together with request
data(device ptr, cmd opcode etc).

This patch also make sure that application id is registered in GATT db
as user_data. This is needed to dispatch ATT request from remote device.
---
android/gatt.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 111 insertions(+), 6 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 616649f..dfd0690 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -82,6 +82,11 @@ struct gatt_app {

/* Valid for client applications */
struct queue *notifications;
+
+ /* Transaction data valid for server application */
+ gatt_db_complete_func_t gatt_db_complete_cb;
+ int32_t pend_trans_id;
+ struct req_data *data;
};

struct element_id {
@@ -3284,9 +3289,100 @@ struct req_data {
struct gatt_device *dev;
uint8_t opcode;

+ bool write_prep;
+ bool write_need_resp;
+ bool write_exec;
+
bool long_read;
};

+static void send_gatt_response(uint8_t opcode, uint16_t handle,
+ uint16_t offset, uint8_t status,
+ uint16_t len, const uint8_t *data,
+ void *req_data,
+ gatt_db_complete_func_t cb)
+{
+ uint16_t length;
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+
+ if (!status) {
+ length = enc_error_resp(opcode, handle, status, pdu,
+ sizeof(pdu));
+ cb(req_data, pdu, length);
+ }
+ /* TODO: Send responses for other commands */
+}
+
+static void write_cb(uint16_t handle, uint16_t offset,
+ const uint8_t *value,
+ size_t len, void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data)
+{
+ struct hal_ev_gatt_server_request_exec_write write_exec_ev;
+ struct hal_ev_gatt_server_request_write write_ev;
+ struct req_data *data = req_data;
+ struct gatt_device *dev = data->dev;
+ struct gatt_app *app;
+ int32_t id = PTR_TO_INT(user_data);
+ static int32_t trans_id = 1;
+ struct app_connection *conn;
+
+ app = find_app_by_id(id);
+ if (!app) {
+ error("gatt: write_cb, cound not found app id");
+ goto failed;
+ }
+
+ conn = find_conn(&dev->bdaddr, app->id);
+ if (!conn) {
+ error("gatt: write_cb, cound not found connection");
+ goto failed;
+ }
+
+ /* Store the request data, complete callback and transaction id */
+ app->pend_trans_id = trans_id++;
+ app->data = data;
+ app->gatt_db_complete_cb = cb;
+
+ if (data->write_exec) {
+ memset(&write_exec_ev, 0, sizeof(write_exec_ev));
+
+ bdaddr2android(&dev->bdaddr, write_exec_ev.bdaddr);
+ write_exec_ev.conn_id = conn->id;
+ write_exec_ev.trans_id = app->pend_trans_id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE,
+ sizeof(write_exec_ev), &write_exec_ev);
+ } else {
+ memset(&write_ev, 0, sizeof(write_ev));
+
+ bdaddr2android(&dev->bdaddr, write_exec_ev.bdaddr);
+ write_ev.attr_handle = handle;
+ write_ev.offset = offset;
+
+ write_ev.conn_id = conn->id;
+ write_ev.trans_id = app->pend_trans_id;
+
+ write_ev.is_prep = data->write_prep;
+ write_ev.need_rsp = data->write_need_resp;
+
+ write_ev.length = len;
+ memcpy(&write_ev.value, value, len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_REQUEST_WRITE,
+ sizeof(write_ev), &write_ev);
+ }
+
+ return;
+
+failed:
+ send_gatt_response(data->opcode, handle, 0, ATT_ECODE_UNLIKELY, 0,
+ NULL, data, cb);
+}
+
static void handle_server_add_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
@@ -3294,12 +3390,13 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len)
struct gatt_app *server;
bt_uuid_t uuid;
uint8_t status;
+ int32_t app_id = cmd->server_if;

DBG("");

memset(&ev, 0, sizeof(ev));

- server = find_app_by_id(cmd->server_if);
+ server = find_app_by_id(app_id);
if (!server) {
status = HAL_STATUS_FAILED;
goto failed;
@@ -3307,11 +3404,13 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len)

android2uuid(cmd->uuid, &uuid);

+ /*FIXME: Handle properties. Register callback if needed. */
ev.char_handle = gatt_db_add_characteristic(gatt_db,
cmd->service_handle,
&uuid, cmd->permissions,
cmd->properties,
- NULL, NULL, NULL);
+ NULL, write_cb,
+ INT_TO_PTR(app_id));
if (!ev.char_handle)
status = HAL_STATUS_FAILED;
else
@@ -3320,7 +3419,7 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len)
failed:
ev.srvc_handle = cmd->service_handle;
ev.status = status;
- ev.server_if = cmd->server_if;
+ ev.server_if = app_id;
ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));

@@ -3338,12 +3437,13 @@ static void handle_server_add_descriptor(const void *buf, uint16_t len)
struct gatt_app *server;
bt_uuid_t uuid;
uint8_t status;
+ int32_t app_id = cmd->server_if;

DBG("");

memset(&ev, 0, sizeof(ev));

- server = find_app_by_id(cmd->server_if);
+ server = find_app_by_id(app_id);
if (!server) {
status = HAL_STATUS_FAILED;
goto failed;
@@ -3351,17 +3451,19 @@ static void handle_server_add_descriptor(const void *buf, uint16_t len)

android2uuid(cmd->uuid, &uuid);

+ /*FIXME: Handle properties. Register callback if needed. */
ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
cmd->service_handle,
&uuid, cmd->permissions,
- NULL, NULL, NULL);
+ NULL, write_cb,
+ INT_TO_PTR(app_id));
if (!ev.descr_handle)
status = HAL_STATUS_FAILED;
else
status = HAL_STATUS_SUCCESS;

failed:
- ev.server_if = cmd->server_if;
+ ev.server_if = app_id;
ev.srvc_handle = cmd->service_handle;
memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
@@ -3930,6 +4032,7 @@ static uint8_t write_request(const uint8_t *cmd, uint16_t cmd_len,
free(req_data);
return ATT_ECODE_INVALID_PDU;
}
+ req_data->write_need_resp = true;
break;
case ATT_OP_PREP_WRITE_REQ:
len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
@@ -3938,6 +4041,7 @@ static uint8_t write_request(const uint8_t *cmd, uint16_t cmd_len,
free(req_data);
return ATT_ECODE_INVALID_PDU;
}
+ req_data->write_prep = true;
break;
case ATT_OP_EXEC_WRITE_REQ:
len = dec_exec_write_req(cmd, cmd_len, value);
@@ -3945,6 +4049,7 @@ static uint8_t write_request(const uint8_t *cmd, uint16_t cmd_len,
return ATT_ECODE_INVALID_PDU;

vlen = 1;
+ req_data->write_exec = true;
break;
default:
error("gatt: Unexpected write type 0x02%x", cmd[0]);
--
1.8.4


2014-04-29 16:47:22

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 33/40] android/gatt: Add support for write request

This patch add support for write request, command and prepare write
---
android/gatt.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 61 insertions(+), 2 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index bd5cf8a..0931374 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3902,6 +3902,61 @@ static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

+static uint8_t write_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint16_t handle;
+ uint16_t offset = 0;
+ uint16_t len;
+ uint8_t value[ATT_DEFAULT_LE_MTU];
+ size_t vlen;
+ struct req_data *req_data;
+
+ req_data = new0(struct req_data, 1);
+ if (!req_data)
+ return ATT_ECODE_UNLIKELY;
+
+ switch (cmd[0]) {
+ case ATT_OP_WRITE_CMD:
+ len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+ if (!len) {
+ free(req_data);
+ return ATT_ECODE_INVALID_PDU;
+ }
+ break;
+ case ATT_OP_WRITE_REQ:
+ len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
+ if (!len) {
+ free(req_data);
+ return ATT_ECODE_INVALID_PDU;
+ }
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
+ value, &vlen);
+ if (!len) {
+ free(req_data);
+ return ATT_ECODE_INVALID_PDU;
+ }
+ break;
+ default:
+ error("gatt: Unexpected write type 0x02%x", cmd[0]);
+ free(req_data);
+ return ATT_ECODE_REQ_NOT_SUPP;
+ }
+
+ req_data->dev = dev;
+ req_data->opcode = cmd[0];
+
+ if (!gatt_db_write(gatt_db, handle, offset, value, vlen, req_data,
+ gatt_request_complete)) {
+ free(req_data);
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3945,13 +4000,17 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
break;
case ATT_OP_WRITE_REQ:
case ATT_OP_WRITE_CMD:
+ case ATT_OP_PREP_WRITE_REQ:
+ status = write_request(ipdu, len, dev);
+ if (!status)
+ return;
+ break;
+ case ATT_OP_EXEC_WRITE_REQ:
case ATT_OP_FIND_BY_TYPE_REQ:
case ATT_OP_HANDLE_CNF:
case ATT_OP_HANDLE_IND:
case ATT_OP_HANDLE_NOTIFY:
case ATT_OP_READ_MULTI_REQ:
- case ATT_OP_PREP_WRITE_REQ:
- case ATT_OP_EXEC_WRITE_REQ:
default:
DBG("Unsupported request 0x%02x", ipdu[0]);
status = ATT_ECODE_REQ_NOT_SUPP;
--
1.8.4


2014-04-29 16:47:24

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 35/40] android/gatt: Move struct req_data upper in the file

This patch is needed by following patches
---
android/gatt.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index cda8697..616649f 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3280,6 +3280,13 @@ failed:
HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
}

+struct req_data {
+ struct gatt_device *dev;
+ uint8_t opcode;
+
+ bool long_read;
+};
+
static void handle_server_add_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
@@ -3735,13 +3742,6 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

-struct req_data {
- struct gatt_device *dev;
- uint8_t opcode;
-
- bool long_read;
-};
-
static void gatt_request_complete(void *data, const uint8_t *pdu, uint16_t len)
{
struct req_data *d = data;
--
1.8.4


2014-04-29 16:47:23

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 34/40] android/gatt: Add support for execute write

---
android/gatt.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index 0931374..cda8697 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3939,6 +3939,13 @@ static uint8_t write_request(const uint8_t *cmd, uint16_t cmd_len,
return ATT_ECODE_INVALID_PDU;
}
break;
+ case ATT_OP_EXEC_WRITE_REQ:
+ len = dec_exec_write_req(cmd, cmd_len, value);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ vlen = 1;
+ break;
default:
error("gatt: Unexpected write type 0x02%x", cmd[0]);
free(req_data);
@@ -4001,11 +4008,11 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
case ATT_OP_WRITE_REQ:
case ATT_OP_WRITE_CMD:
case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
status = write_request(ipdu, len, dev);
if (!status)
return;
break;
- case ATT_OP_EXEC_WRITE_REQ:
case ATT_OP_FIND_BY_TYPE_REQ:
case ATT_OP_HANDLE_CNF:
case ATT_OP_HANDLE_IND:
--
1.8.4


2014-04-29 16:47:21

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 32/40] shared/gatt: Add support for write request

---
src/shared/gatt-db.c | 23 +++++++++++++++++++++++
src/shared/gatt-db.h | 4 ++++
2 files changed, 27 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index dfe1a76..e562695 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -704,6 +704,29 @@ bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,

a->read_func(handle, offset, req_data, cb, a->user_data);

+ return true;
+}
+
+bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
+ const uint8_t *value, size_t len, void *req_data,
+ gatt_db_complete_func_t cb)
+{
+ struct gatt_db_service *service;
+ uint16_t service_handle;
+ struct gatt_db_attribute *a;
+
+ service = queue_find(db->services, find_service_for_handle,
+ INT_TO_PTR(handle));
+ if (!service)
+ return false;
+
+ service_handle = service->attributes[0]->handle;
+
+ a = service->attributes[handle - service_handle];
+ if (!a || !a->write_func)
+ return false;
+
+ a->write_func(handle, offset, value, len, req_data, cb, a->user_data);

return true;
}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 9a56c51..90fa66e 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -112,3 +112,7 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,

bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
void *req_data, gatt_db_complete_func_t complete_cb);
+
+bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
+ const uint8_t *value, size_t len, void *req_data,
+ gatt_db_complete_func_t complete_cb);
--
1.8.4


2014-04-29 16:47:20

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 31/40] android/gatt: Add Find info gatt server cmd handling

From: Grzegorz Kolodziejczyk <[email protected]>

The Find Information Request is used to obtain the mapping of
attribute handles with their associated types. This allows a
client to discover the list of attributes and their types on
a server.
---
android/gatt.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 95a248d..bd5cf8a 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3842,6 +3842,66 @@ static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

+static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
+ uint8_t *rsp, size_t rsp_size,
+ uint16_t *length)
+{
+ struct queue *q;
+ struct gatt_db_find_information *last_element;
+ struct copy_att_list_data l;
+ struct att_data_list *adl;
+ uint16_t start, end;
+ uint16_t num;
+ uint8_t format;
+ uint16_t len;
+
+ DBG("");
+
+ len = dec_find_info_req(cmd, cmd_len, &start, &end);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ q = queue_new();
+ if (!q)
+ return ATT_ECODE_UNLIKELY;
+
+ gatt_db_find_information(gatt_db, start, end, q);
+
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_ATTR_NOT_FOUND;
+ }
+
+ len = queue_length(q);
+
+ last_element = queue_peek_head(q);
+
+ if (last_element->uuid.type == BT_UUID16) {
+ num = sizeof(uint16_t);
+ format = ATT_FIND_INFO_RESP_FMT_16BIT;
+ } else {
+ num = sizeof(uint128_t);
+ format = ATT_FIND_INFO_RESP_FMT_128BIT;
+ }
+
+ adl = att_data_list_alloc(len, num + sizeof(uint16_t));
+
+ l.iterator = 0;
+ l.adl = adl;
+
+ queue_foreach(q, copy_to_att_list_type, &l);
+
+ len = enc_find_info_resp(format, adl, rsp, rsp_size);
+ if (!len)
+ return ATT_ECODE_UNLIKELY;
+
+ *length = len;
+ att_data_list_free(adl);
+ queue_destroy(q, free);
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3880,6 +3940,9 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
&length);
break;
case ATT_OP_FIND_INFO_REQ:
+ status = find_info_handle(ipdu, len, opdu, sizeof(opdu),
+ &length);
+ break;
case ATT_OP_WRITE_REQ:
case ATT_OP_WRITE_CMD:
case ATT_OP_FIND_BY_TYPE_REQ:
--
1.8.4


2014-04-29 16:47:19

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 30/40] android/gatt: Add MTU request cmd handling

From: Grzegorz Kolodziejczyk <[email protected]>

The Exchange MTU Request is used by the client to inform the server of
client's maximum receive MTU size and request the server to respond with
its maximum receive MTU size.
---
android/gatt.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 88a285f..95a248d 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -138,6 +138,8 @@ struct gatt_device {
guint watch_id;
guint server_id;

+ guint mtu_size;
+
int ref;
int conn_cnt;
};
@@ -3799,6 +3801,47 @@ static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

+static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
+ uint8_t *rsp, size_t rsp_size,
+ struct gatt_device *dev,
+ uint16_t *length)
+{
+ uint16_t mtu, imtu;
+ GIOChannel *io;
+ GError *gerr = NULL;
+ uint16_t len;
+
+ DBG("");
+
+ len = dec_mtu_req(cmd, cmd_len, &mtu);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ if (mtu < ATT_DEFAULT_LE_MTU)
+ return ATT_ECODE_REQ_NOT_SUPP;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ dev->mtu_size = MIN(mtu, imtu);
+ g_attrib_set_mtu(dev->attrib, dev->mtu_size);
+
+ len = enc_mtu_resp(imtu, rsp, rsp_size);
+ if (!len)
+ return ATT_ECODE_UNLIKELY;
+
+ *length = len;
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3833,6 +3876,9 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
break;

case ATT_OP_MTU_REQ:
+ status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
+ &length);
+ break;
case ATT_OP_FIND_INFO_REQ:
case ATT_OP_WRITE_REQ:
case ATT_OP_WRITE_CMD:
--
1.8.4


2014-04-29 16:47:18

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 29/40] android/gatt: Add support to read request

Added support for read and read blob

Note: There are request data created and provided to the user in the
read_cb. User is responsible to free this data.
---
android/gatt.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index cd9ce86..88a285f 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3736,8 +3736,69 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
struct req_data {
struct gatt_device *dev;
uint8_t opcode;
+
+ bool long_read;
};

+static void gatt_request_complete(void *data, const uint8_t *pdu, uint16_t len)
+{
+ struct req_data *d = data;
+
+ if (pdu)
+ g_attrib_send(d->dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+ else
+ error("gatt: gatt_request_complete: pdu not present ?!");
+
+ free(d);
+}
+
+static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
+ struct gatt_device *dev)
+{
+ uint16_t handle;
+ uint16_t len;
+ uint16_t offset = 0;
+ struct req_data *req_data;
+
+ DBG("");
+
+ req_data = new0(struct req_data, 1);
+ if (!req_data)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ switch (cmd[0]) {
+ case ATT_OP_READ_BLOB_REQ:
+ len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
+ if (!len) {
+ free(req_data);
+ return ATT_ECODE_INVALID_PDU;
+ }
+ req_data->long_read = true;
+ break;
+ case ATT_OP_READ_REQ:
+ len = dec_read_req(cmd, cmd_len, &handle);
+ if (!len) {
+ free(req_data);
+ return ATT_ECODE_INVALID_PDU;
+ }
+ break;
+ default:
+ error("gatt: Unexpected read type 0x%02x", cmd[0]);
+ free(req_data);
+ return ATT_ECODE_REQ_NOT_SUPP;
+ }
+
+ req_data->dev = dev;
+ req_data->opcode = cmd[0];
+
+ if (!gatt_db_read(gatt_db, handle, offset, req_data, gatt_request_complete)) {
+ free(req_data);
+ return ATT_ECODE_UNLIKELY;
+ }
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3765,6 +3826,12 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
break;
case ATT_OP_READ_REQ:
case ATT_OP_READ_BLOB_REQ:
+ status = read_request(ipdu, len, dev);
+ if (!status)
+ /* Response will be sent in callback */
+ return;
+ break;
+
case ATT_OP_MTU_REQ:
case ATT_OP_FIND_INFO_REQ:
case ATT_OP_WRITE_REQ:
--
1.8.4


2014-04-29 16:47:17

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 28/40] android/gatt: Move struct req_data up in the file

It is needed by following patch
---
android/gatt.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 7c1c959..cd9ce86 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3733,6 +3733,11 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

+struct req_data {
+ struct gatt_device *dev;
+ uint8_t opcode;
+};
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3871,11 +3876,6 @@ static struct gap_srvc_handles gap_srvc_data;
#define APPEARANCE_GENERIC_PHONE 0x0040
#define PERIPHERAL_PRIVACY_DISABLE 0x00

-struct req_data {
- struct gatt_device *dev;
- uint8_t opcode;
-};
-
static void gap_read_cb(uint16_t handle, uint16_t offset, void *req_data,
gatt_db_complete_func_t cb,
void *user_data)
--
1.8.4


2014-04-29 16:47:16

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 27/40] shared/gatt: Add support to read from database

---
src/shared/gatt-db.c | 36 ++++++++++++++++++++++++++++++++++++
src/shared/gatt-db.h | 3 +++
2 files changed, 39 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index b716ae6..dfe1a76 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -671,3 +671,39 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,

queue_foreach(db->services, find_information, &data);
}
+
+static bool find_service_for_handle(const void *data, const void *user_data)
+{
+ const struct gatt_db_service *service = data;
+ uint16_t handle = PTR_TO_INT(user_data);
+ uint16_t start, end;
+
+ start = service->attributes[0]->handle;
+ end = start + service->num_handles;
+
+ return (start <= handle) && (handle < end);
+}
+
+bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
+ void *req_data, gatt_db_complete_func_t cb)
+{
+ struct gatt_db_service *service;
+ uint16_t service_handle;
+ struct gatt_db_attribute *a;
+
+ service = queue_find(db->services, find_service_for_handle,
+ INT_TO_PTR(handle));
+ if (!service)
+ return false;
+
+ service_handle = service->attributes[0]->handle;
+
+ a = service->attributes[handle - service_handle];
+ if (!a || !a->read_func)
+ return false;
+
+ a->read_func(handle, offset, req_data, cb, a->user_data);
+
+
+ return true;
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 921e896..9a56c51 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -109,3 +109,6 @@ struct gatt_db_find_information {
void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
uint16_t end_handle,
struct queue *queue);
+
+bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
+ void *req_data, gatt_db_complete_func_t complete_cb);
--
1.8.4


2014-04-29 16:47:15

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 26/40] android/gatt: Add support for ATT read by type

From: Grzegorz Kolodziejczyk <[email protected]>

---
android/gatt.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 09d4307..7c1c959 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3616,6 +3616,19 @@ static void copy_to_att_list(void *data, void *user_data)
memcpy(&value[4], group->value, group->len);
}

+static void copy_to_att_list_type(void *data, void *user_data)
+{
+ struct copy_att_list_data *l = user_data;
+ struct gatt_db_handle_value *hdl_val = data;
+ uint8_t *value;
+
+ value = l->adl->data[l->iterator++];
+
+ put_le16(hdl_val->handle, value);
+
+ memcpy(&value[2], hdl_val->value, hdl_val->length);
+}
+
static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
uint8_t *rsp, size_t rsp_size,
uint16_t *length)
@@ -3667,6 +3680,59 @@ static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}

+static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
+ uint8_t *rsp, size_t rsp_size,
+ uint16_t *length)
+{
+ uint16_t start, end;
+ uint16_t len;
+ bt_uuid_t uuid;
+ struct queue *q;
+ struct att_data_list *adl;
+ struct copy_att_list_data l;
+ struct gatt_db_handle_value *h;
+
+ DBG("");
+
+ len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ q = queue_new();
+ if (!q)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ gatt_db_read_by_type(gatt_db, start, end, uuid, q);
+
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_ATTR_NOT_FOUND;
+ }
+
+ len = queue_length(q);
+ h = queue_peek_tail(q);
+
+ /* Element here is handle + value*/
+ adl = att_data_list_alloc(len, sizeof(uint16_t) + h->length);
+ if (adl == NULL) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
+ l.iterator = 0;
+ l.adl = adl;
+
+ queue_foreach(q, copy_to_att_list_type, &l);
+
+ len = enc_read_by_type_resp(adl, rsp, rsp_size);
+ *length = len;
+
+ att_data_list_free(adl);
+ queue_destroy(q, free);
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -3690,6 +3756,8 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
&length);
break;
case ATT_OP_READ_BY_TYPE_REQ:
+ status = read_by_type(ipdu, len, opdu, sizeof(opdu), &length);
+ break;
case ATT_OP_READ_REQ:
case ATT_OP_READ_BLOB_REQ:
case ATT_OP_MTU_REQ:
--
1.8.4


2014-04-29 16:47:14

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 25/40] android/gatt: Add support for ATT read by group type

---
android/gatt.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index dd1053f..09d4307 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3597,17 +3597,98 @@ static const struct ipc_handler cmd_handlers[] = {
sizeof(struct hal_cmd_gatt_server_send_response) },
};

+struct copy_att_list_data {
+ int iterator;
+ struct att_data_list *adl;
+};
+
+static void copy_to_att_list(void *data, void *user_data)
+{
+ struct copy_att_list_data *l = user_data;
+ struct gatt_db_group *group = data;
+ uint8_t *value;
+
+ value = l->adl->data[l->iterator++];
+
+ put_le16(group->handle, value);
+ put_le16(group->end_group, &value[2]);
+
+ memcpy(&value[4], group->value, group->len);
+}
+
+static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
+ uint8_t *rsp, size_t rsp_size,
+ uint16_t *length)
+{
+ uint16_t start, end;
+ uint16_t len;
+ bt_uuid_t uuid;
+ struct queue *q;
+ struct att_data_list *adl;
+ struct copy_att_list_data l;
+ struct gatt_db_group *d;
+
+ len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid);
+ if (!len)
+ return ATT_ECODE_INVALID_PDU;
+
+ q = queue_new();
+ if (!q)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ gatt_db_read_by_group_type(gatt_db, start, end, uuid, q);
+
+ if (queue_isempty(q)) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_ATTR_NOT_FOUND;
+ }
+
+ len = queue_length(q);
+ d = queue_peek_head(q);
+
+ /* Element contains start/end handle + size of uuid */
+ adl = att_data_list_alloc(len, 2 * sizeof(uint16_t) + d->len);
+ if (adl == NULL) {
+ queue_destroy(q, NULL);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
+ l.iterator = 0;
+ l.adl = adl;
+
+ queue_foreach(q, copy_to_att_list, &l);
+
+ len = enc_read_by_grp_resp(adl, rsp, rsp_size);
+ *length = len;
+
+ att_data_list_free(adl);
+ queue_destroy(q, free);
+
+ return 0;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
{
struct gatt_device *dev = user_data;
uint8_t opdu[ATT_DEFAULT_LE_MTU];
uint8_t status;
uint16_t length = 0;
+ size_t vlen;
+ uint8_t *value = g_attrib_get_buffer(dev->attrib, &vlen);

DBG("op 0x%02x", ipdu[0]);

+ if (len > vlen) {
+ error("Too much data on ATT socket %p", value);
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
switch (ipdu[0]) {
case ATT_OP_READ_BY_GROUP_REQ:
+ status = read_by_group_type(ipdu, len, opdu, sizeof(opdu),
+ &length);
+ break;
case ATT_OP_READ_BY_TYPE_REQ:
case ATT_OP_READ_REQ:
case ATT_OP_READ_BLOB_REQ:
@@ -3633,7 +3714,7 @@ done:
length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
ATT_DEFAULT_LE_MTU);

- g_attrib_send(dev->attrib, 0, opdu, length , NULL, NULL, NULL);
+ g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
}

static void create_listen_connections(void *data, void *user_data)
--
1.8.4


2014-04-29 16:47:13

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 24/40] shared/gatt: Add function to read by type

From: Marcin Kraglak <[email protected]>

It will return list of attributes with given type.
---
src/shared/gatt-db.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-db.h | 20 +++++++++
2 files changed, 139 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index c3645f1..b716ae6 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -552,3 +552,122 @@ void gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle,

queue_foreach(db->services, find_by_type_value, &data);
}
+
+struct read_by_type_data {
+ struct queue *queue;
+ bt_uuid_t uuid;
+ uint16_t start_handle;
+ uint16_t end_handle;
+};
+
+static void read_by_type(void *data, void *user_data)
+{
+ struct read_by_type_data *search_data = user_data;
+ struct gatt_db_service *service = data;
+ struct gatt_db_attribute *attribute;
+ struct gatt_db_handle_value *value;
+ int i;
+
+ if (!service->active)
+ return;
+
+ for (i = 0; i < service->num_handles; i++) {
+ attribute = service->attributes[i];
+ if (!attribute)
+ continue;
+
+ if (attribute->handle < search_data->start_handle)
+ continue;
+
+ if (attribute->handle > search_data->end_handle)
+ return;
+
+ if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+ continue;
+
+ value = malloc0(sizeof(struct gatt_db_handle_value) +
+ attribute->value_len);
+ if (!value)
+ return;
+
+ value->handle = attribute->handle;
+ value->length = attribute->value_len;
+ if (attribute->value_len)
+ memcpy(value->value, attribute->value, value->length);
+
+ if (!queue_push_tail(search_data->queue, value))
+ free(value);
+ }
+}
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ struct queue *queue)
+{
+ struct read_by_type_data data;
+ data.uuid = type;
+ data.start_handle = start_handle;
+ data.end_handle = end_handle;
+ data.queue = queue;
+
+ queue_foreach(db->services, read_by_type, &data);
+}
+
+
+struct find_information_data {
+ struct queue *queue;
+ uint16_t start_handle;
+ uint16_t end_handle;
+};
+
+static void find_information(void *data, void *user_data)
+{
+ struct find_information_data *search_data = user_data;
+ struct gatt_db_service *service = data;
+ struct gatt_db_attribute *attribute;
+ struct gatt_db_find_information *value;
+ int i;
+
+ if (!service->active)
+ return;
+
+ /* Check if service is in range */
+ if (service->attributes[service->num_handles - 1]->handle <
+ search_data->start_handle)
+ return;
+
+ for (i = 0; i < service->num_handles; i++) {
+ attribute = service->attributes[i];
+ if (!attribute)
+ continue;
+
+ if (attribute->handle < search_data->start_handle)
+ continue;
+
+ if (attribute->handle > search_data->end_handle)
+ return;
+
+ value = new0(struct gatt_db_find_information, 1);
+ if (!value)
+ return;
+
+ value->handle = attribute->handle;
+ value->uuid = attribute->uuid;
+ if (!queue_push_tail(search_data->queue, value))
+ free(value);
+ }
+}
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ struct queue *queue)
+{
+ struct find_information_data data;
+
+ data.start_handle = start_handle;
+ data.end_handle = end_handle;
+ data.queue = queue;
+
+ queue_foreach(db->services, find_information, &data);
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index e582f83..921e896 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -89,3 +89,23 @@ void gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle,
const uint8_t *value,
uint16_t length,
struct queue *queue);
+
+struct gatt_db_handle_value {
+ uint16_t handle;
+ uint16_t length;
+ uint8_t value[0];
+};
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ struct queue *queue);
+
+struct gatt_db_find_information {
+ uint16_t handle;
+ bt_uuid_t uuid;
+};
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ struct queue *queue);
--
1.8.4


2014-04-29 16:47:12

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 23/40] shared/gatt: Add function to find by type

From: Marcin Kraglak <[email protected]>

It will look for attributes with given uuid and value.
---
src/shared/gatt-db.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-db.h | 12 +++++++++
2 files changed, 84 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 26fdc84..c3645f1 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -480,3 +480,75 @@ void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,

queue_foreach(db->services, read_by_group_type, &data);
}
+
+struct find_by_type_value_data {
+ struct queue *queue;
+ bt_uuid_t uuid;
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t value_length;
+ const uint8_t *value;
+};
+
+static void find_by_type_value(void *data, void *user_data)
+{
+ struct find_by_type_value_data *search_data = user_data;
+ struct gatt_db_service *service = data;
+ struct gatt_db_attribute *attribute;
+ struct gatt_db_range *range;
+ int i;
+
+ if (!service->active)
+ return;
+
+ for (i = 0; i < service->num_handles; i++) {
+ attribute = service->attributes[i];
+
+ if (!attribute)
+ continue;
+
+ if ((attribute->handle < search_data->start_handle) ||
+ (attribute->handle > search_data->end_handle))
+ continue;
+
+ if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+ continue;
+
+ if (attribute->value_len != search_data->value_length)
+ continue;
+
+ if (!memcmp(attribute->value, search_data->value,
+ attribute->value_len))
+ continue;
+
+ range = new0(struct gatt_db_range, 1);
+ if (!range)
+ return;
+
+ range->handle = attribute->handle;
+ range->end_group = service->attributes[0]->handle +
+ service->num_handles - 1;
+
+ if (!queue_push_tail(search_data->queue, range))
+ free(range);
+ }
+}
+
+void gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ const uint8_t *value,
+ uint16_t length,
+ struct queue *queue)
+{
+ struct find_by_type_value_data data;
+
+ data.uuid = type;
+ data.start_handle = start_handle;
+ data.end_handle = end_handle;
+ data.queue = queue;
+ data.value_length = length;
+ data.value = value;
+
+ queue_foreach(db->services, find_by_type_value, &data);
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index b6e963d..e582f83 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -77,3 +77,15 @@ void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
uint16_t end_handle,
const bt_uuid_t type,
struct queue *queue);
+
+struct gatt_db_range {
+ uint16_t handle;
+ uint16_t end_group;
+};
+
+void gatt_db_find_by_type_value(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ const uint8_t *value,
+ uint16_t length,
+ struct queue *queue);
--
1.8.4


2014-04-29 16:47:11

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 22/40] shared/gatt: Add function to read by group type

From: Marcin Kraglak <[email protected]>

It will return list of gropu values, starting handle, end handle and
service attrtbute value.
---
src/shared/gatt-db.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/shared/gatt-db.h | 13 ++++++++++
2 files changed, 84 insertions(+)

diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 8675dcc..26fdc84 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -409,3 +409,74 @@ bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle,

return true;
}
+
+struct read_by_group_type_data {
+ struct queue *queue;
+ bt_uuid_t uuid;
+ uint16_t start_handle;
+ uint16_t end_handle;
+ uint16_t uuid_size;
+ bool stop_search;
+};
+
+static void read_by_group_type(void *data, void *user_data)
+{
+ struct read_by_group_type_data *search_data = user_data;
+ struct gatt_db_service *service = data;
+ struct gatt_db_group *group;
+
+ if (!service->active)
+ return;
+
+ /* Don't want more results as they have different size*/
+ if (search_data->stop_search)
+ return;
+
+ if (bt_uuid_cmp(&search_data->uuid, &service->attributes[0]->uuid))
+ return;
+
+ if (service->attributes[0]->handle < search_data->start_handle)
+ return;
+
+ /* Remember size of uuid */
+ if (!search_data->uuid_size) {
+ search_data->uuid_size = service->attributes[0]->value_len;
+ } else if (search_data->uuid_size !=
+ service->attributes[0]->value_len) {
+
+ /* Don't want more results. This is last */
+ search_data->stop_search = true;
+ return;
+ }
+
+ group = malloc0(sizeof(struct gatt_db_group) +
+ service->attributes[0]->value_len);
+ if (!group)
+ return;
+
+ group->len = service->attributes[0]->value_len;
+ memcpy(group->value, service->attributes[0]->value, group->len);
+ group->handle = service->attributes[0]->handle;
+ group->end_group = service->attributes[0]->handle +
+ service->num_handles - 1;
+
+ if (!queue_push_tail(search_data->queue, group))
+ free(group);
+}
+
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ struct queue *queue)
+{
+ struct read_by_group_type_data data;
+
+ data.uuid = type;
+ data.start_handle = start_handle;
+ data.end_handle = end_handle;
+ data.queue = queue;
+ data.uuid_size = 0;
+ data.stop_search = false;
+
+ queue_foreach(db->services, read_by_group_type, &data);
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index c1b7b41..b6e963d 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -64,3 +64,16 @@ uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle,

bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle,
bool active);
+
+struct gatt_db_group {
+ uint16_t handle;
+ uint16_t end_group;
+ uint16_t len;
+ uint8_t value[0];
+};
+
+/* Returns queue with struct gatt_db_group */
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t type,
+ struct queue *queue);
--
1.8.4


2014-04-29 16:47:09

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 20/40] shared: Extend write callback with offset

In case of prepare right offset is needed.
---
src/shared/gatt-db.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 5a6513a..c1b7b41 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -38,7 +38,8 @@ typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset,
gatt_db_complete_func_t cb,
void *user_data);

-typedef void (*gatt_db_write_t) (uint16_t handle, const uint8_t *value,
+typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset,
+ const uint8_t *value,
size_t len, void *req_data,
gatt_db_complete_func_t cb,
void *user_data);
--
1.8.4


2014-04-29 16:47:10

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 21/40] android/gatt: Register GATT service

From: Jakub Tyszkowski <[email protected]>

This adds database record about gatt's 'services changed'
characteristic. Proper flag in device is being set to mark it for
notification if gatt services are changed.
---
android/gatt.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 9719637..dd1053f 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -133,6 +133,8 @@ struct gatt_device {
GIOChannel *att_io;
struct queue *services;

+ bool notify_services_changed;
+
guint watch_id;
guint server_id;

@@ -3891,6 +3893,43 @@ static void register_device_info_service(void)
gatt_db_service_set_active(gatt_db, srvc_handle, true);
}

+static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
+ const uint8_t *val, size_t len,
+ void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct req_data *data = req_data;
+ uint16_t length;
+
+ /* Set services changed notification flag */
+ data->dev->notify_services_changed = !!(*val);
+
+ length = enc_write_resp(pdu);
+
+ cb(data, pdu, length);
+}
+
+static void register_gatt_service(void)
+{
+ bt_uuid_t uuid;
+ uint16_t srvc_handle;
+
+ DBG("");
+
+ bt_uuid16_create(&uuid, 0x1801);
+ srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 3);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_INDICATE,
+ NULL, gatt_srvc_change_register_cb,
+ NULL);
+
+ gatt_db_service_set_active(gatt_db, srvc_handle, true);
+}
+
static int start_listen_socket(void)
{
GError *gerr = NULL;
@@ -3959,6 +3998,7 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)

register_gap_service();
register_device_info_service();
+ register_gatt_service();

return true;
}
--
1.8.4


2014-04-29 16:47:08

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 19/40] android/gatt: Register device information service

From: Jakub Tyszkowski <[email protected]>

This adds placeholder data to device information service. To get
real data we should figure out the best way to get Android's system
properties and expose them to profiles for reading.
---
android/gatt.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 88e583c..9719637 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3800,6 +3800,97 @@ static void register_gap_service(void)

gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
}
+
+/* TODO: Get those data from device possible via androig/bluetooth.c */
+static struct device_info {
+ const char *manufacturer_name;
+ const char *system_id;
+ const char *model_number;
+ const char *serial_number;
+ const char *firmware_rev;
+ const char *hardware_rev;
+ const char *software_rev;
+} device_info = {
+ .manufacturer_name = "BlueZ",
+ .system_id = "BlueZ for Android",
+ .model_number = "model no",
+ .serial_number = "serial no",
+ .firmware_rev = "firmware rev",
+ .hardware_rev = "hardware rev",
+ .software_rev = "software rev",
+};
+
+static void device_info_read_cb(uint16_t handle, uint16_t offset,
+ void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ char *buf = user_data;
+ uint16_t len;
+
+ len = enc_read_resp((uint8_t *) buf, strlen(buf), pdu, sizeof(pdu));
+
+ cb(req_data, pdu, len);
+}
+
+static void register_device_info_service(void)
+{
+ bt_uuid_t uuid;
+ uint16_t srvc_handle;
+
+ DBG("");
+
+ /* Device Information Service */
+ bt_uuid16_create(&uuid, 0x180a);
+ srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 15);
+
+ /* User data are not const hence (void *) cast is used */
+ bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.system_id);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.model_number);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.serial_number);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.firmware_rev);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.hardware_rev);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.software_rev);
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
+ gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+ GATT_CHR_PROP_READ,
+ device_info_read_cb, NULL,
+ (void *) device_info.manufacturer_name);
+
+ gatt_db_service_set_active(gatt_db, srvc_handle, true);
+}
+
static int start_listen_socket(void)
{
GError *gerr = NULL;
@@ -3867,6 +3958,7 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
error("Could not start GATT listening");

register_gap_service();
+ register_device_info_service();

return true;
}
--
1.8.4


2014-04-29 16:47:06

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 17/40] android/gatt: Add register GAP Service

Register GAP service with device name characteristic,
appearance and peripheral privacy flag
---
android/gatt.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index cba1db6..88e583c 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3706,6 +3706,100 @@ static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
error("gatt: Could not attache to server");
}

+struct gap_srvc_handles {
+ uint16_t srvc;
+
+ /* Characteristics */
+ uint16_t dev_name;
+ uint16_t appear;
+ uint16_t priv;
+};
+
+static struct gap_srvc_handles gap_srvc_data;
+
+#define APPEARANCE_GENERIC_PHONE 0x0040
+#define PERIPHERAL_PRIVACY_DISABLE 0x00
+
+struct req_data {
+ struct gatt_device *dev;
+ uint8_t opcode;
+};
+
+static void gap_read_cb(uint16_t handle, uint16_t offset, void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint16_t len;
+
+ DBG("");
+
+ if (handle == gap_srvc_data.dev_name) {
+ const char *name = bt_get_adapter_name();
+
+ len = enc_read_resp((uint8_t *)name, strlen(name),
+ pdu, sizeof(pdu));
+ goto done;
+ }
+
+ if (handle == gap_srvc_data.appear) {
+ uint8_t val[2];
+ put_le16(APPEARANCE_GENERIC_PHONE, val);
+ len = enc_read_resp(val, sizeof(val), pdu, sizeof(pdu));
+ goto done;
+ }
+
+ if (handle == gap_srvc_data.priv) {
+ uint8_t val = PERIPHERAL_PRIVACY_DISABLE;
+ len = enc_read_resp(&val, sizeof(val), pdu, sizeof(pdu));
+ goto done;
+ }
+
+ error("gatt: Unknown handle 0x%02x", handle);
+ len = enc_error_resp(ATT_OP_READ_REQ, handle,
+ ATT_ECODE_UNLIKELY, pdu, sizeof(pdu));
+
+done:
+ cb(req_data, pdu, len);
+}
+
+static void register_gap_service(void)
+{
+ bt_uuid_t uuid;
+
+ /* GAP UUID */
+ bt_uuid16_create(&uuid, 0x1800);
+ gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7);
+
+ /* Device name characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ gap_srvc_data.dev_name =
+ gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+ &uuid, 0,
+ GATT_CHR_PROP_READ,
+ gap_read_cb, NULL,
+ NULL);
+
+ /* Appearance */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ gap_srvc_data.appear =
+ gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+ &uuid, 0,
+ GATT_CHR_PROP_READ,
+ gap_read_cb, NULL,
+ NULL);
+
+ /* Pripheral privacy flag */
+ bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
+ gap_srvc_data.priv =
+ gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+ &uuid, 0,
+ GATT_CHR_PROP_READ,
+ gap_read_cb, NULL,
+ NULL);
+
+ gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
+}
static int start_listen_socket(void)
{
GError *gerr = NULL;
@@ -3772,6 +3866,8 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
if (start_listen_socket() < 0)
error("Could not start GATT listening");

+ register_gap_service();
+
return true;
}

--
1.8.4


2014-04-29 16:47:07

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 18/40] gatt: Add some characteristics uuids

These characteristics uuids will be used in following patch
---
lib/uuid.h | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/lib/uuid.h b/lib/uuid.h
index 237145b..701e248 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -118,6 +118,13 @@ extern "C" {
#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
+#define GATT_CHARAC_SYSTEM_ID 0x2A23
+#define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24
+#define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25
+#define GATT_CHARAC_FIRMWARE_REVISION_STRING 0x2A26
+#define GATT_CHARAC_HARDWARE_REVISION_STRING 0x2A27
+#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28
+#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29

/* GATT Characteristic Descriptors */
#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
--
1.8.4


2014-04-29 16:47:05

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 16/40] android/bluetooth: Add function for getting device Android name

This will be used by GATT HAL
---
android/bluetooth.c | 5 +++++
android/bluetooth.h | 1 +
2 files changed, 6 insertions(+)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 03b8576..655fb1e 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -1198,6 +1198,11 @@ uint8_t bt_get_device_android_type(const bdaddr_t *addr)
return get_device_android_type(dev);
}

+const char *bt_get_adapter_name(void)
+{
+ return adapter.name;
+}
+
static bool rssi_above_threshold(int old, int new)
{
/* only 8 dBm or more */
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 649c9ba..dddbf20 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -48,3 +48,4 @@ bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
void *user_data);

uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+const char *bt_get_adapter_name(void);
--
1.8.4


2014-04-29 16:47:04

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 15/40] shared/gatt: Add complete command callback

GATT server calls db and provides request data. Once characteristic
callback is done, complete callback shall be fired with request data
and pdu which should be sent to remote device.
---
src/shared/gatt-db.h | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 73f5119..5a6513a 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -30,11 +30,17 @@ uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
bool primary, uint16_t num_handles);
bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);

+typedef void (*gatt_db_complete_func_t)(void *req_data, const uint8_t *pdu,
+ uint16_t len);
+
typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset,
- void *req_data, void *user_data);
+ void *req_data,
+ gatt_db_complete_func_t cb,
+ void *user_data);

typedef void (*gatt_db_write_t) (uint16_t handle, const uint8_t *value,
size_t len, void *req_data,
+ gatt_db_complete_func_t cb,
void *user_data);

uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
--
1.8.4


2014-04-29 16:47:03

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 14/40] shared/gatt: Extend read callback with offset

Offset is needed for read blob.
---
src/shared/gatt-db.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index d1443cb..73f5119 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -30,8 +30,8 @@ uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
bool primary, uint16_t num_handles);
bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);

-typedef void (*gatt_db_read_t) (uint16_t handle, void *req_data,
- void *user_data);
+typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset,
+ void *req_data, void *user_data);

typedef void (*gatt_db_write_t) (uint16_t handle, const uint8_t *value,
size_t len, void *req_data,
--
1.8.4


2014-04-29 16:47:02

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 13/40] shared: Use pointer for request data instead of int

This is because user of database might need to get in callback some more
information then request id. It is to avoid keep in database user any
list of request_id data.
---
src/shared/gatt-db.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index f68f4b3..d1443cb 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -30,12 +30,12 @@ uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
bool primary, uint16_t num_handles);
bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);

-typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t request_id,
+typedef void (*gatt_db_read_t) (uint16_t handle, void *req_data,
void *user_data);

-typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t request_id,
- const uint8_t *value, size_t len,
- void *user_data);
+typedef void (*gatt_db_write_t) (uint16_t handle, const uint8_t *value,
+ size_t len, void *req_data,
+ void *user_data);

uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
const bt_uuid_t *uuid,
--
1.8.4


2014-04-29 16:47:01

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 12/40] android/gatt: Add ATT msg handler

This patch adds skeleton for ATT msg handler and also attach it to the
attrib on connect_cb and connect_event
---
android/gatt.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index f035e3f..cba1db6 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -134,6 +134,7 @@ struct gatt_device {
struct queue *services;

guint watch_id;
+ guint server_id;

int ref;
int conn_cnt;
@@ -494,6 +495,9 @@ static void connection_cleanup(struct gatt_device *device)
device->att_io = NULL;
}

+ if (device->server_id > 0)
+ g_attrib_unregister(device->attrib, device->server_id);
+
if (device->attrib) {
GAttrib *attrib = device->attrib;
device->attrib = NULL;
@@ -962,6 +966,8 @@ static void send_app_connect_notifications(void *data, void *user_data)
send_app_connect_notify(conn, con_data->status);
}

+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -997,6 +1003,12 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
disconnected_cb, dev);

+ dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+ GATTRIB_ALL_HANDLES,
+ att_handler, dev, NULL);
+ if (dev->server_id == 0)
+ error("gatt: Could not attach to server");
+
device_set_state(dev, DEVICE_CONNECTED);

status = GATT_SUCCESS;
@@ -3583,6 +3595,45 @@ static const struct ipc_handler cmd_handlers[] = {
sizeof(struct hal_cmd_gatt_server_send_response) },
};

+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+{
+ struct gatt_device *dev = user_data;
+ uint8_t opdu[ATT_DEFAULT_LE_MTU];
+ uint8_t status;
+ uint16_t length = 0;
+
+ DBG("op 0x%02x", ipdu[0]);
+
+ switch (ipdu[0]) {
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_MTU_REQ:
+ case ATT_OP_FIND_INFO_REQ:
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ case ATT_OP_HANDLE_CNF:
+ case ATT_OP_HANDLE_IND:
+ case ATT_OP_HANDLE_NOTIFY:
+ case ATT_OP_READ_MULTI_REQ:
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
+ default:
+ DBG("Unsupported request 0x%02x", ipdu[0]);
+ status = ATT_ECODE_REQ_NOT_SUPP;
+ goto done;
+ }
+
+done:
+ if (status)
+ length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+ ATT_DEFAULT_LE_MTU);
+
+ g_attrib_send(dev->attrib, 0, opdu, length , NULL, NULL, NULL);
+}
+
static void create_listen_connections(void *data, void *user_data)
{
struct gatt_device *dev = user_data;
@@ -3648,7 +3699,11 @@ static void connect_event(GIOChannel *io, GError *gerr, void *user_data)

queue_foreach(app_connections, send_app_connect_notifications, &data);

- /* TODO: Attach to attrib db */
+ dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+ GATTRIB_ALL_HANDLES,
+ att_handler, dev, NULL);
+ if (!dev->server_id)
+ error("gatt: Could not attache to server");
}

static int start_listen_socket(void)
--
1.8.4


2014-04-29 16:47:00

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 11/40] android/gatt: Assume that each server wants waits for connection

---
android/gatt.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 9e950ec..f035e3f 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -575,6 +575,13 @@ static int register_app(const uint8_t *uuid, gatt_app_type_t app_type)
return 0;
}

+ if ((app->type == APP_SERVER) &&
+ !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
+ error("gatt: Cannot push server on the list");
+ destroy_gatt_app(app);
+ return 0;
+ }
+
return app->id;
}

--
1.8.4


2014-04-29 16:46:59

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 10/40] android/gatt: Add listening socket for GATT

For now we do listen on BLE transport
---
android/gatt.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)

diff --git a/android/gatt.c b/android/gatt.c
index 115fb1a..9e950ec 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -156,6 +156,8 @@ static struct queue *app_connections = NULL;
static struct queue *listen_apps = NULL;
static struct gatt_db *gatt_db = NULL;

+static GIOChannel *listening_sk = NULL;
+
static void bt_le_discovery_stop_cb(void);

static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
@@ -3574,6 +3576,98 @@ static const struct ipc_handler cmd_handlers[] = {
sizeof(struct hal_cmd_gatt_server_send_response) },
};

+static void create_listen_connections(void *data, void *user_data)
+{
+ struct gatt_device *dev = user_data;
+ int32_t id = PTR_TO_INT(data);
+ struct gatt_app *app;
+
+ app = find_app_by_id(id);
+ if (app)
+ create_connection(dev, app);
+}
+
+static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+{
+ struct gatt_device *dev;
+ GAttrib *attrib;
+ uint8_t dst_type;
+ bdaddr_t src, dst;
+ struct connect_data data;
+
+ DBG("");
+
+ if (gerr) {
+ error("gatt: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ bt_io_get(io, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ dev = create_device(&dst);
+ if (!dev) {
+ error("gatt: Could not create device");
+ return;
+ }
+
+ dev->bdaddr_type = dst_type;
+
+ attrib = g_attrib_new(io);
+ if (!attrib) {
+ error("gatt: unable to create new GAttrib instance");
+ destroy_device(dev);
+ return;
+ }
+
+ dev->attrib = attrib;
+ dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ disconnected_cb, dev);
+
+ queue_foreach(listen_apps, create_listen_connections, dev);
+
+ data.dev = dev;
+ data.status = GATT_SUCCESS;
+ device_set_state(dev, DEVICE_CONNECTED);
+
+ queue_foreach(app_connections, send_app_connect_notifications, &data);
+
+ /* TODO: Attach to attrib db */
+}
+
+static int start_listen_socket(void)
+{
+ GError *gerr = NULL;
+
+ DBG("");
+
+ /*For now only listen on BLE */
+ listening_sk = bt_io_listen(connect_event, NULL,
+ &listening_sk, NULL, &gerr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (!listening_sk) {
+ int ret = gerr->code;
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return ret;
+ }
+
+ return 0;
+}
+
bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
{
DBG("");
@@ -3613,6 +3707,9 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));

+ if (start_listen_socket() < 0)
+ error("Could not start GATT listening");
+
return true;
}

--
1.8.4


2014-04-29 16:46:58

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 09/40] android/gatt: Add descriptor implementation

From: Marcin Kraglak <[email protected]>

It will add characteristic descriptor attribute to local database.
---
android/gatt.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index e888268..115fb1a 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3302,30 +3302,41 @@ failed:
static void handle_server_add_descriptor(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_descriptor *cmd = buf;
- char uuidstr[MAX_LEN_UUID_STR];
+ struct hal_ev_gatt_server_descriptor_added ev;
struct gatt_app *server;
- bt_uuid_t descr_uuid;
+ bt_uuid_t uuid;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- android2uuid(cmd->uuid, &descr_uuid);
- bt_uuid_to_string(&descr_uuid, uuidstr, MAX_LEN_UUID_STR);
-
- /* TODO: Add descriptor to attribute database */
- DBG("Add descriptor: server: %d, srvc_hnd: %d, uuid: %s, perm: %d",
- cmd->server_if, cmd->service_handle, uuidstr, cmd->permissions);
+ android2uuid(cmd->uuid, &uuid);

- status = HAL_STATUS_SUCCESS;
+ ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
+ cmd->service_handle,
+ &uuid, cmd->permissions,
+ NULL, NULL, NULL);
+ if (!ev.descr_handle)
+ status = HAL_STATUS_FAILED;
+ else
+ status = HAL_STATUS_SUCCESS;

failed:
+ ev.server_if = cmd->server_if;
+ ev.srvc_handle = cmd->service_handle;
+ memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
}
--
1.8.4


2014-04-29 16:46:54

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 05/40] android/gatt: Add included service implementation

From: Marcin Kraglak <[email protected]>

It will add included service attribute to database.
---
android/gatt.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index e4ef1ea..f8398fc 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3219,25 +3219,38 @@ failed:
static void handle_server_add_included_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_inc_service *cmd = buf;
+ struct hal_ev_gatt_server_inc_srvc_added ev;
struct gatt_app *server;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- /* TODO: Add included service to attribute database */
- DBG("Add included service: server: %d, srvc_hnd: %d, incl_hnd: %d",
- cmd->server_if, cmd->service_handle, cmd->included_handle);
+ ev.incl_srvc_handle = gatt_db_add_included_service(gatt_db,
+ cmd->service_handle,
+ cmd->included_handle);
+ if (!ev.incl_srvc_handle) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }

status = HAL_STATUS_SUCCESS;
-
failed:
+ ev.srvc_handle = cmd->service_handle;
+ ev.status = status;
+ ev.server_if = cmd->server_if;
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
}
--
1.8.4


2014-04-29 16:46:55

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 06/40] android/gatt: Add characteristic implementation

From: Marcin Kraglak <[email protected]>

It will add characteristic declaration and value attributes to
local database.
---
android/gatt.c | 34 +++++++++++++++++++++++-----------
1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index f8398fc..7522c65 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3258,31 +3258,43 @@ failed:
static void handle_server_add_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
- char uuidstr[MAX_LEN_UUID_STR];
+ struct hal_ev_gatt_server_characteristic_added ev;
struct gatt_app *server;
- bt_uuid_t char_uuid;
+ bt_uuid_t uuid;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- android2uuid(cmd->uuid, &char_uuid);
- bt_uuid_to_string(&char_uuid, uuidstr, MAX_LEN_UUID_STR);
+ android2uuid(cmd->uuid, &uuid);

- /* TODO: Add characteristic to database */
- DBG("Add char: server: %d, uuid: %s, srvc_hnd: %d, prop: %d, perm: %d",
- cmd->server_if, uuidstr, cmd->service_handle, cmd->properties,
- cmd->permissions);
-
- status = HAL_STATUS_SUCCESS;
+ ev.char_handle = gatt_db_add_characteristic(gatt_db,
+ cmd->service_handle,
+ &uuid, cmd->permissions,
+ cmd->properties,
+ NULL, NULL, NULL);
+ if (!ev.char_handle)
+ status = HAL_STATUS_FAILED;
+ else
+ status = HAL_STATUS_SUCCESS;

failed:
+ ev.srvc_handle = cmd->service_handle;
+ ev.status = status;
+ ev.server_if = cmd->server_if;
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status);
}
--
1.8.4


2014-04-29 16:46:53

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 04/40] android/gatt: Add implementation of delete service

From: Marcin Kraglak <[email protected]>

It will delete sarvice from local database
---
android/gatt.c | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 6e578e1..e4ef1ea 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3361,27 +3361,37 @@ failed:
static void handle_server_delete_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_delete_service *cmd = buf;
+ struct hal_ev_gatt_server_service_deleted ev;
struct gatt_app *server;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- /* TODO: delete service from attribute database */
- DBG("Delete service: server: %d, srvc_hnd: %d", cmd->server_if,
- cmd->service_handle);
+ if (!gatt_db_remove_service(gatt_db, cmd->service_handle)) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }

status = HAL_STATUS_SUCCESS;

failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.srvc_handle = cmd->service_handle;
+ ev.server_if = cmd->server_if;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
+ HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
}

static void handle_server_send_indication(const void *buf, uint16_t len)
--
1.8.4


2014-04-29 16:46:52

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 03/40] android/gatt: Add service functionality

From: Marcin Kraglak <[email protected]>

It will Add service to local database.
---
android/gatt.c | 40 ++++++++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 0bb5a70..6e578e1 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -43,6 +43,7 @@
#include "utils.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
@@ -153,6 +154,7 @@ static struct queue *gatt_devices = NULL;
static struct queue *app_connections = NULL;

static struct queue *listen_apps = NULL;
+static struct gatt_db *gatt_db = NULL;

static void bt_le_discovery_stop_cb(void);

@@ -3175,32 +3177,43 @@ static void handle_server_disconnect(const void *buf, uint16_t len)
static void handle_server_add_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_service *cmd = buf;
- char uuidstr[MAX_LEN_UUID_STR];
+ struct hal_ev_gatt_server_service_added ev;
struct gatt_app *server;
- struct element_id srvc_id;
uint8_t status;
+ bt_uuid_t uuid;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
- bt_uuid_to_string(&srvc_id.uuid, uuidstr, MAX_LEN_UUID_STR);
+ android2uuid(cmd->srvc_id.uuid, &uuid);

- /* TODO: execute attribute database transaction */
- DBG("Add primary service: server: %d, srvc_uuid: %s, num_handles: %d",
- cmd->server_if, uuidstr, cmd->num_handles);
+ ev.srvc_handle = gatt_db_add_service(gatt_db, &uuid,
+ cmd->srvc_id.is_primary,
+ cmd->num_handles);
+ if (!ev.srvc_handle) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }

status = HAL_STATUS_SUCCESS;

failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.srvc_id = cmd->srvc_id;
+ ev.server_if = cmd->server_if;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_SERVER_ADD_SERVICE, status);
+ HAL_OP_GATT_SERVER_ADD_SERVICE, status);
}

static void handle_server_add_included_service(const void *buf, uint16_t len)
@@ -3503,9 +3516,10 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
gatt_apps = queue_new();
app_connections = queue_new();
listen_apps = queue_new();
+ gatt_db = gatt_db_new();

if (!gatt_devices || !gatt_apps || !listen_apps ||
- !app_connections) {
+ !app_connections || !gatt_db) {
error("gatt: Failed to allocate memory for queues");

queue_destroy(gatt_apps, NULL);
@@ -3520,6 +3534,9 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
queue_destroy(listen_apps, NULL);
listen_apps = NULL;

+ gatt_db_destroy(gatt_db);
+ gatt_db = NULL;
+
return false;
}

@@ -3551,4 +3568,7 @@ void bt_gatt_unregister(void)

queue_destroy(listen_apps, NULL);
listen_apps = NULL;
+
+ gatt_db_destroy(gatt_db);
+ gatt_db = NULL;
}
--
1.8.4


2014-04-29 16:46:57

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 08/40] android/gatt: Add stop service command handling

From: Marcin Kraglak <[email protected]>

It will stop service in local database.
---
android/gatt.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 2913007..e888268 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3372,25 +3372,33 @@ failed:
static void handle_server_stop_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_stop_service *cmd = buf;
+ struct hal_ev_gatt_server_service_stopped ev;
struct gatt_app *server;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

- status = HAL_STATUS_SUCCESS;
-
- /* TODO: stop service in attribute database */
- DBG("Stop service: server: %d, srvc_hnd: %d", cmd->server_if,
- cmd->service_handle);
+ if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, false))
+ status = HAL_STATUS_FAILED;
+ else
+ status = HAL_STATUS_SUCCESS;

failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.server_if = cmd->server_if;
+ ev.srvc_handle = cmd->service_handle;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_STOP_SERVICE, status);
}
--
1.8.4


2014-04-29 16:46:56

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 07/40] android/gatt: Add handling of start service command

From: Marcin Kraglak <[email protected]>

It will start service added to local database.
---
android/gatt.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 7522c65..2913007 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3333,26 +3333,38 @@ failed:
static void handle_server_start_service(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_start_service *cmd = buf;
+ struct hal_ev_gatt_server_service_started ev;
struct gatt_app *server;
uint8_t status;

DBG("");

+ memset(&ev, 0, sizeof(ev));
+
server = find_app_by_id(cmd->server_if);
if (!server) {
- error("gatt: server_if=%d not found", cmd->server_if);
status = HAL_STATUS_FAILED;
goto failed;
}

/* TODO: support BR/EDR (cmd->transport) */
- /* TODO: activate service in attribute database */
- DBG("Start service: server: %d, srvc_hnd: %d, transport_layer: %d",
- cmd->server_if, cmd->service_handle, cmd->transport);
+
+ if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, true)) {
+ /* we ignore service now */
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }

status = HAL_STATUS_SUCCESS;

failed:
+ ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+ ev.server_if = cmd->server_if;
+ ev.srvc_handle = cmd->service_handle;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev);
+
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_START_SERVICE, status);
}
--
1.8.4


2014-04-29 16:46:51

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 02/40] android/gatt: Remove redundant find function parameter

From: Jakub Tyszkowski <[email protected]>

As client and server apps id is guaranteed to be unique, there is no
need for this search function parameter.
---
android/gatt.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index f9eff30..0bb5a70 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -1248,8 +1248,7 @@ static void handle_client_unregister(const void *buf, uint16_t len)
HAL_OP_GATT_CLIENT_UNREGISTER, status);
}

-static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id,
- int32_t app_type)
+static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
{
struct app_connection conn_match;
struct gatt_device *dev = NULL;
@@ -2891,7 +2890,7 @@ static void handle_client_register_for_notification(const void *buf,

android2bdaddr(&cmd->bdaddr, &addr);

- conn = find_conn(&addr, cmd->client_if, APP_CLIENT);
+ conn = find_conn(&addr, cmd->client_if);
if (!conn) {
status = HAL_STATUS_FAILED;
goto failed;
@@ -2997,7 +2996,7 @@ static void handle_client_deregister_for_notification(const void *buf,

android2bdaddr(&cmd->bdaddr, &addr);

- conn = find_conn(&addr, cmd->client_if, APP_CLIENT);
+ conn = find_conn(&addr, cmd->client_if);
if (!conn) {
status = HAL_STATUS_FAILED;
goto failed;
--
1.8.4


2014-04-29 16:46:50

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v2 01/40] android/gatt: Rename listen_clients to listen_apps

This is because on this list there will be also servers id
---
android/gatt.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index 4775ecc..f9eff30 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -152,7 +152,7 @@ static struct queue *gatt_apps = NULL;
static struct queue *gatt_devices = NULL;
static struct queue *app_connections = NULL;

-static struct queue *listen_clients = NULL;
+static struct queue *listen_apps = NULL;

static void bt_le_discovery_stop_cb(void);

@@ -1380,7 +1380,7 @@ static void set_advertising_cb(uint8_t status, void *user_data)
* 2. Stop succeed
*/
if ((l->start && status) || (!l->start && !status))
- queue_remove(listen_clients, INT_TO_PTR(l->client_id));
+ queue_remove(listen_apps, INT_TO_PTR(l->client_id));

free(l);
}
@@ -1401,7 +1401,7 @@ static void handle_client_listen(const void *buf, uint16_t len)
goto reply;
}

- listening_client = queue_find(listen_clients, match_by_value,
+ listening_client = queue_find(listen_apps, match_by_value,
INT_TO_PTR(cmd->client_if));
/* Start listening */
if (cmd->start) {
@@ -1410,7 +1410,7 @@ static void handle_client_listen(const void *buf, uint16_t len)
goto reply;
}

- if (!queue_push_tail(listen_clients,
+ if (!queue_push_tail(listen_apps,
INT_TO_PTR(cmd->client_if))) {
error("gatt: Could not put client on listen queue");
status = HAL_STATUS_FAILED;
@@ -1418,7 +1418,7 @@ static void handle_client_listen(const void *buf, uint16_t len)
}

/* If listen is already on just return success*/
- if (queue_length(listen_clients) > 1) {
+ if (queue_length(listen_apps) > 1) {
status = HAL_STATUS_SUCCESS;
goto reply;
}
@@ -1435,8 +1435,8 @@ static void handle_client_listen(const void *buf, uint16_t len)
* In case there is more listening clients don't stop
* advertising
*/
- if (queue_length(listen_clients) > 1) {
- queue_remove(listen_clients,
+ if (queue_length(listen_apps) > 1) {
+ queue_remove(listen_apps,
INT_TO_PTR(cmd->client_if));
status = HAL_STATUS_SUCCESS;
goto reply;
@@ -3503,9 +3503,9 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
gatt_devices = queue_new();
gatt_apps = queue_new();
app_connections = queue_new();
- listen_clients = queue_new();
+ listen_apps = queue_new();

- if (!gatt_devices || !gatt_apps || !listen_clients ||
+ if (!gatt_devices || !gatt_apps || !listen_apps ||
!app_connections) {
error("gatt: Failed to allocate memory for queues");

@@ -3518,8 +3518,8 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
queue_destroy(app_connections, NULL);
app_connections = NULL;

- queue_destroy(listen_clients, NULL);
- listen_clients = NULL;
+ queue_destroy(listen_apps, NULL);
+ listen_apps = NULL;

return false;
}
@@ -3550,6 +3550,6 @@ void bt_gatt_unregister(void)
queue_destroy(gatt_devices, destroy_device);
gatt_devices = NULL;

- queue_destroy(listen_clients, NULL);
- listen_clients = NULL;
+ queue_destroy(listen_apps, NULL);
+ listen_apps = NULL;
}
--
1.8.4