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.
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: Add comment about event type being sent
android/gatt: Register device information service
android/gatt: Register GATT service
Lukasz Rymanowski (20):
android/gatt: Add listening socket for GATT
android/gatt: Add ATT msg handler
shared: Use pointer for request data instead of int
shared/gatt: Extend read callback with offset
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 callback to 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/gatt.c | 1176 +++++++++++++++++++++++++++++++++++++++++++++++---
android/hal-gatt.c | 7 +-
android/hal-msg.h | 3 +
lib/uuid.h | 7 +
src/shared/gatt-db.c | 320 ++++++++++++++
src/shared/gatt-db.h | 62 ++-
6 files changed, 1515 insertions(+), 60 deletions(-)
--
1.8.4
Hi Johan,
On Tue, Apr 29, 2014 at 10:16 AM, Johan Hedberg <[email protected]> wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> 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).
>> ---
>> android/gatt.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 100 insertions(+), 2 deletions(-)
>>
>> diff --git a/android/gatt.c b/android/gatt.c
>> index 2eb5b18..09eae80 100644
>> --- a/android/gatt.c
>> +++ b/android/gatt.c
>> @@ -82,6 +82,10 @@ struct gatt_app {
>>
>> /* Valid for client applications */
>> struct queue *notifications;
>> +
>> + /* Transaction data valid for server application */
>> + int32_t pend_trans_id;
>
> Could we please be consistent with async operation id types. I.e. use an
> unsigned int here.
>
This one handled in v4
> 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
Hi Johan
On Tue, Apr 29, 2014 at 2:56 PM, Johan Hedberg <[email protected]> wrote:
> Hi Lukasz,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> On Tue, Apr 29, 2014 at 10:13 AM, Johan Hedberg <[email protected]> wrote:
>> > On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> >> +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_UNLIKELY;
>> >> +
>> >> + 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)) {
>> >> + free(req_data);
>> >> + return ATT_ECODE_UNLIKELY;
>> >> + }
>> >> +
>> >> + return 0;
>> >> +}
>> >
>> > Where is req_data freed in the case that gatt_db_read succeeds?
>>
>> It is freed later in the callback. Will put comment about that.
>
> Which callback is that? E.g. in 13/36 the gap_read_cb function does not
> free req_data.
Your previous comment realized me that we are missing that in read
callback for gatt service, device
information service and service changed. it is now fixed.
>
> In general this kind of API design seems quite strange to me. Probably
> the information contained in struct req_data can be made fixed for the
> gatt-db API instead of something chosen by the user (in this case
> android/gatt.c), and probably doesn't have to be dynamically allocated
> to begin with.
well I agree. I will do something like request_complete_cb which should be
called in the end of request and there we do free.
Also for now this callback will take aswell response pdu and length,
so it will be one place
where msg is send back to remote device.
it will be in v2
>
> Johan
Hi Lukasz,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> On Tue, Apr 29, 2014 at 10:13 AM, Johan Hedberg <[email protected]> wrote:
> > On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> >> +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_UNLIKELY;
> >> +
> >> + 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)) {
> >> + free(req_data);
> >> + return ATT_ECODE_UNLIKELY;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >
> > Where is req_data freed in the case that gatt_db_read succeeds?
>
> It is freed later in the callback. Will put comment about that.
Which callback is that? E.g. in 13/36 the gap_read_cb function does not
free req_data.
In general this kind of API design seems quite strange to me. Probably
the information contained in struct req_data can be made fixed for the
gatt-db API instead of something chosen by the user (in this case
android/gatt.c), and probably doesn't have to be dynamically allocated
to begin with.
Johan
Hi Johan,
On Tue, Apr 29, 2014 at 10:13 AM, Johan Hedberg <[email protected]> wrote:
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> +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_UNLIKELY;
>> +
>> + 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)) {
>> + free(req_data);
>> + return ATT_ECODE_UNLIKELY;
>> + }
>> +
>> + return 0;
>> +}
>
> Where is req_data freed in the case that gatt_db_read succeeds?
It is freed later in the callback. Will put comment about that.
>
> 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
Thanks
Lukasz
Hi Johan,
On Tue, Apr 29, 2014 at 10:12 AM, Johan Hedberg <[email protected]> wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> +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 = (void *)l->adl->data[l->iterator++];
>
> Same thing here with the type cast.
fix in v2
>
> 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
Thanks
Łukasz
Hi Johan,
On Tue, Apr 29, 2014 at 10:08 AM, Johan Hedberg <[email protected]> wrote:
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> 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 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 90 insertions(+)
>>
>> diff --git a/android/gatt.c b/android/gatt.c
>> index d2b1684..2ad5936 100644
>> --- a/android/gatt.c
>> +++ b/android/gatt.c
>> @@ -3811,6 +3811,95 @@ static void register_gap_service(void)
>>
>> gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
>> }
>> +
>> +/* TODO: Figure out the best way for this to be not hard coded. */
>> +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",
>> +};
>
> Again, you'll want to hook up at least some of these, like the system_id
> to information from android/bluetooth.c
We will handle it later on when adding customisation.
For now will put better todo comment .
>
>> + 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);
>
> Space after all of the (void *) type casts here.
Fix in v2. BTW checkpatch did confusion :).
> You'll also probably
> want to add a comment why it's needed, i.e. because these variables are
> const while the user_data parameter is not.
fix in v2
>
> 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
Thanks
\Łukasz
Hi Johan,
On Tue, Apr 29, 2014 at 10:05 AM, Johan Hedberg <[email protected]> wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> 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 5ca22e2..d2b1684 100644
>> --- a/android/gatt.c
>> +++ b/android/gatt.c
>> @@ -161,6 +161,8 @@ static struct gatt_db *gatt_db = NULL;
>>
>> static GIOChannel *listening_sk = NULL;
>>
>> +static char device_name[249] = "BlueZ for Android";
>
> Shouldn't this be hooked up to adapter.name in android/bluetooth.c?
Yes, forgot to add todo but actually will do it now.
>
>> +struct gap_srvc_handles {
>> + uint16_t srvc;
>> +
>> + /*Characteristics */
>
> Space after /*
fix in v2
>
>> + if (handle == gap_srvc_data.dev_name) {
>> + len = enc_read_resp((uint8_t *)&device_name[0],
>
> Space after typecast (our user space coding style convention).
Not valid in v2 anymore
>
>> +static void register_gap_service(void)
>> +{
>> + bt_uuid_t uuid;
>> +
>> + /*GAP UUID */
>
> Space after /*
fix in v2
>
> 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
Thanks
Lukasz
Hi Johan,
On Tue, Apr 29, 2014 at 10:03 AM, Johan Hedberg <[email protected]> wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> + if (device->server_id)
>> + g_attrib_unregister(device->attrib, device->server_id);
>
> I think the consistent style for async operation id checks is id > 0
Fix in v2
>
>> + 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");
>
> And id == 0 here
Fix in v2
>
> 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
Thanks
Łukasz
Hi Johan
On Tue, Apr 29, 2014 at 10:01 AM, Johan Hedberg <[email protected]> wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
>> +static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
>> +{
>> + struct gatt_device *dev = NULL;
>
fix in v2
> This looks like an unnecessary initialization upon declaration, looking
> at how the variable is handled in the rest of the function.
>
>> + /*TODO: Attach to attrib db */
>
> Space after /*
fix in v2
>
>> + if (listening_sk == NULL) {
>
> I think we're trying to make the coding style consistent by using
> if (!ptr)
>
fix in v2
> 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
Thanks
Lukasz
Hi Johan,
On Tuesday 29 of April 2014 10:57:18 Johan Hedberg wrote:
> Hi,
>
> On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> > ---
> > android/gatt.c | 5 +++++
> > 1 file changed, 5 insertions(+)
> >
> > diff --git a/android/gatt.c b/android/gatt.c
> > index 0e81e7d..ed4ee75 100644
> > --- a/android/gatt.c
> > +++ b/android/gatt.c
> > @@ -599,6 +599,11 @@ failed:
> > static void send_client_disconnection_notify(struct app_connection *connection,
> > int32_t status)
> > {
> > + /* disconnect and connect events are binary the same so its safe to pass
> > + * hal_ev_gatt_client_disconnect struct even for connect event.
> > + * Note that server do this the right way, having single event for
> > + * connection state notification.
> > + */
>
> I believe the user space commenting style (if we try to be consistent)
> is to have an empty line in the beginning of a multi-line comment.
Yeah, we are a bit inconsistent wrt multi-line comments in android folder.
I'll push bulk patch that fix this.
--
Best regards,
Szymon Janc
Hi,
On 04/29/2014 03:14 AM, Lukasz Rymanowski wrote:
> From: Jakub Tyszkowski <[email protected]>
>
> ---
> android/gatt.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index 0e81e7d..ed4ee75 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -599,6 +599,11 @@ failed:
> static void send_client_disconnection_notify(struct app_connection *connection,
> int32_t status)
> {
> + /* disconnect and connect events are binary the same so its safe to pass
> + * hal_ev_gatt_client_disconnect struct even for connect event.
> + * Note that server do this the right way, having single event for
> + * connection state notification.
> + */
> struct hal_ev_gatt_client_disconnect ev;
>
> ev.client_if = connection->app->id;
>
This function was already changed to send only disconnect event and
another function sends connect event. Please ignore this patch.
------------------
Jakub Tyszkowski
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> 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).
> ---
> android/gatt.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 100 insertions(+), 2 deletions(-)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index 2eb5b18..09eae80 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -82,6 +82,10 @@ struct gatt_app {
>
> /* Valid for client applications */
> struct queue *notifications;
> +
> + /* Transaction data valid for server application */
> + int32_t pend_trans_id;
Could we please be consistent with async operation id types. I.e. use an
unsigned int here.
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +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)) {
> + free(req_data);
> + return ATT_ECODE_UNLIKELY;
> + }
> +
> + return 0;
> +}
Same question here: where is req_data freed if gatt_db_read succeeds?
Johan
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +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_UNLIKELY;
> +
> + 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)) {
> + free(req_data);
> + return ATT_ECODE_UNLIKELY;
> + }
> +
> + return 0;
> +}
Where is req_data freed in the case that gatt_db_read succeeds?
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +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 = (void *)l->adl->data[l->iterator++];
Same thing here with the type cast.
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +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 = (void *)l->adl->data[l->iterator++];
Is this type-cast really needed? Looking at the definition of struct
att_data_list in attrib/att.h it seems it should not be needed. Anyway,
if it is needed there's a missing space again there.
Johan
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> 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 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 90 insertions(+)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index d2b1684..2ad5936 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -3811,6 +3811,95 @@ static void register_gap_service(void)
>
> gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
> }
> +
> +/* TODO: Figure out the best way for this to be not hard coded. */
> +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",
> +};
Again, you'll want to hook up at least some of these, like the system_id
to information from android/bluetooth.c
> + 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);
Space after all of the (void *) type casts here. You'll also probably
want to add a comment why it's needed, i.e. because these variables are
const while the user_data parameter is not.
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> 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 5ca22e2..d2b1684 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -161,6 +161,8 @@ static struct gatt_db *gatt_db = NULL;
>
> static GIOChannel *listening_sk = NULL;
>
> +static char device_name[249] = "BlueZ for Android";
Shouldn't this be hooked up to adapter.name in android/bluetooth.c?
> +struct gap_srvc_handles {
> + uint16_t srvc;
> +
> + /*Characteristics */
Space after /*
> + if (handle == gap_srvc_data.dev_name) {
> + len = enc_read_resp((uint8_t *)&device_name[0],
Space after typecast (our user space coding style convention).
> +static void register_gap_service(void)
> +{
> + bt_uuid_t uuid;
> +
> + /*GAP UUID */
Space after /*
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> + if (device->server_id)
> + g_attrib_unregister(device->attrib, device->server_id);
I think the consistent style for async operation id checks is id > 0
> + 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");
And id == 0 here
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> +static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
> +{
> + struct gatt_device *dev = NULL;
This looks like an unnecessary initialization upon declaration, looking
at how the variable is handled in the rest of the function.
> + /*TODO: Attach to attrib db */
Space after /*
> + if (listening_sk == NULL) {
I think we're trying to make the coding style consistent by using
if (!ptr)
Johan
Hi,
On Tue, Apr 29, 2014, Lukasz Rymanowski wrote:
> ---
> android/gatt.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index 0e81e7d..ed4ee75 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -599,6 +599,11 @@ failed:
> static void send_client_disconnection_notify(struct app_connection *connection,
> int32_t status)
> {
> + /* disconnect and connect events are binary the same so its safe to pass
> + * hal_ev_gatt_client_disconnect struct even for connect event.
> + * Note that server do this the right way, having single event for
> + * connection state notification.
> + */
I believe the user space commenting style (if we try to be consistent)
is to have an empty line in the beginning of a multi-line comment.
Johan
Hi
On 29 April 2014 03:14, Lukasz Rymanowski <[email protected]> wrote:
> 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.
>
Forgot to mention that for now we do handle ATT requests in android
folder but once bt_att will be ready we will move to use that io.
> 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: Add comment about event type being sent
> android/gatt: Register device information service
> android/gatt: Register GATT service
>
> Lukasz Rymanowski (20):
> android/gatt: Add listening socket for GATT
> android/gatt: Add ATT msg handler
> shared: Use pointer for request data instead of int
> shared/gatt: Extend read callback with offset
> 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 callback to 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/gatt.c | 1176 +++++++++++++++++++++++++++++++++++++++++++++++---
> android/hal-gatt.c | 7 +-
> android/hal-msg.h | 3 +
> lib/uuid.h | 7 +
> src/shared/gatt-db.c | 320 ++++++++++++++
> src/shared/gatt-db.h | 62 ++-
> 6 files changed, 1515 insertions(+), 60 deletions(-)
>
> --
> 1.8.4
>
\Lukasz
Hi
On 29 April 2014 03:14, Lukasz Rymanowski <[email protected]> wrote:
> With this patch Android app can send indication to remote device
> ---
> android/gatt.c | 30 +++++++++++++++++++++++++++++-
> 1 file changed, 29 insertions(+), 1 deletion(-)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index 3e291cf..74d36bb 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -3666,10 +3666,38 @@ 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;
> + 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;
> + }
> +
> + 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));
> +
> + g_attrib_send(conn->device->attrib , 0, pdu, length, NULL, NULL , NULL);
> +
> + 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
>
Ignore this patch
\Lukasz
---
android/gatt.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 70ebb06..6368e2c 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3316,13 +3316,39 @@ static void send_gatt_response(uint8_t opcode, uint16_t handle, uint16_t offset,
uint16_t length;
uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ DBG("");
+
if (!status) {
length = enc_error_resp(opcode, handle, status, pdu,
sizeof(pdu));
g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
return;
}
- /* 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");
+ return;
+ }
+
+ g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
}
static void read_cb(uint16_t handle, uint16_t offset,
@@ -3648,10 +3674,40 @@ 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;
+ 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;
+ }
+
+ if (cmd->trans_id != conn->app->pend_trans_id) {
+ error("gatt: trans_id != pend_trans_id (%d!=%d)",
+ cmd->trans_id, conn->app->pend_trans_id);
+
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ send_gatt_response(cmd->conn_id, cmd->handle, cmd->offset, cmd->status,
+ cmd->len, cmd->data, conn->device);
+
+ free(conn->app->data);
+ conn->app->data = NULL;
+
+ conn->app->pend_trans_id = 0;
+
+ 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
With this patch Android app can send indication to remote device
---
android/gatt.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 6368e2c..91a34e0 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3666,10 +3666,38 @@ 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;
+ 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;
+ }
+
+ 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));
+
+ g_attrib_send(conn->device->attrib , 0, pdu, length, NULL, NULL , NULL);
+
+ 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
This patch attach read callback to descriptors and characteristics
registered by Android app.
---
android/gatt.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 50 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 09eae80..70ebb06 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3325,6 +3325,52 @@ static void send_gatt_response(uint8_t opcode, uint16_t handle, uint16_t offset,
/* TODO: Send responses for other commands */
}
+static void read_cb(uint16_t handle, uint16_t offset,
+ void *req_data, void *user_data)
+{
+ struct hal_ev_gatt_server_request_read ev;
+ struct req_data *data = req_data;
+ 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_connection_by_id(app->id);
+ if (!conn) {
+ error("gatt: read_cb, cound not found connection");
+ goto failed;
+ }
+
+ memset(&ev, 0, sizeof(ev));
+
+ app->data = req_data;
+ app->pend_trans_id = trans_id++;
+
+ bdaddr2android(&data->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->dev);
+ free(data);
+}
+
static void write_cb(uint16_t handle, uint16_t offset,
const uint8_t *value,
size_t len, void *req_data,
@@ -3418,7 +3464,8 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len)
cmd->service_handle,
&uuid, cmd->permissions,
cmd->properties,
- NULL, write_cb, NULL);
+ read_cb, write_cb,
+ NULL);
if (!ev.char_handle)
status = HAL_STATUS_FAILED;
else
@@ -3462,7 +3509,8 @@ 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, NULL);
+ read_cb, write_cb,
+ NULL);
if (!ev.descr_handle)
status = HAL_STATUS_FAILED;
else
--
1.8.4
---
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 3bbae2b..dfab8f9 100644
--- a/android/hal-gatt.c
+++ b/android/hal-gatt.c
@@ -1236,9 +1236,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
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).
---
android/gatt.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 100 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 2eb5b18..09eae80 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -82,6 +82,10 @@ struct gatt_app {
/* Valid for client applications */
struct queue *notifications;
+
+ /* Transaction data valid for server application */
+ int32_t pend_trans_id;
+ struct req_data *data;
};
struct element_id {
@@ -3297,9 +3301,98 @@ 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,
+ struct gatt_device *dev)
+{
+ uint16_t length;
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+
+ if (!status) {
+ length = enc_error_resp(opcode, handle, status, pdu,
+ sizeof(pdu));
+ g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+ return;
+ }
+ /* 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,
+ 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_connection_by_id(app->id);
+ if (!conn) {
+ error("gatt: write_cb, cound not found connection");
+ goto failed;
+ }
+
+ app->pend_trans_id = trans_id++;
+ app->data = data;
+
+ 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, dev);
+ free(data);
+}
+
static void handle_server_add_characteristic(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
@@ -3320,11 +3413,12 @@ 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, NULL);
if (!ev.char_handle)
status = HAL_STATUS_FAILED;
else
@@ -3364,10 +3458,11 @@ 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, NULL);
if (!ev.descr_handle)
status = HAL_STATUS_FAILED;
else
@@ -3931,6 +4026,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,
@@ -3939,6 +4035,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);
@@ -3946,6 +4043,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
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 581ccc0..2eb5b18 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3293,6 +3293,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;
@@ -3748,13 +3755,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 uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
--
1.8.4
---
android/gatt.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 661e294..581ccc0 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3940,6 +3940,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
This patch add support for write request, command and prepare write
---
android/gatt.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 10450cd..661e294 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3903,6 +3903,60 @@ 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)) {
+ 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;
@@ -3946,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
---
src/shared/gatt-db.c | 23 +++++++++++++++++++++++
src/shared/gatt-db.h | 3 +++
2 files changed, 26 insertions(+)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 42b0903..527e4a7 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -706,3 +706,26 @@ bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
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)
+{
+ 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, a->user_data);
+
+ return true;
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index b63d1f6..2c77a07 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -106,3 +106,6 @@ 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);
+
+bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
+ const uint8_t *value, size_t len, void *req_data);
--
1.8.4
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 9b72640..10450cd 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3843,6 +3843,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;
@@ -3881,6 +3941,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
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 da5a7c8..9b72640 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;
};
@@ -3800,6 +3802,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;
@@ -3834,6 +3877,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
Added support for read and read blob
---
android/gatt.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/android/gatt.c b/android/gatt.c
index 609775e..da5a7c8 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3749,8 +3749,57 @@ 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 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_UNLIKELY;
+
+ 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)) {
+ 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;
@@ -3778,6 +3827,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
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 d442a49..609775e 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3746,6 +3746,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;
@@ -3885,11 +3890,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,
void *user_data)
{
--
1.8.4
---
src/shared/gatt-db.c | 35 +++++++++++++++++++++++++++++++++++
src/shared/gatt-db.h | 3 +++
2 files changed, 38 insertions(+)
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index b716ae6..42b0903 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -671,3 +671,38 @@ 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)
+{
+ 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, a->user_data);
+
+ return true;
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index a6da4e2..b63d1f6 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -103,3 +103,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);
--
1.8.4
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 eb1a910..d442a49 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3629,6 +3629,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 = (void *)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)
@@ -3680,6 +3693,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_UNLIKELY;
+
+ 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_UNLIKELY;
+ }
+
+ 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;
@@ -3703,6 +3769,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
---
android/gatt.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 9f3a796..eb1a910 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3610,17 +3610,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 = (void *)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_IO;
+
+ 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_UNLIKELY;
+ }
+
+ 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:
@@ -3641,12 +3722,13 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
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);
+ g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
}
static void create_listen_connections(void *data, void *user_data)
--
1.8.4
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 6b7897c..a6da4e2 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -83,3 +83,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
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 9d5e214..6b7897c 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -71,3 +71,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
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 314e09b..9d5e214 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -58,3 +58,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
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 | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/android/gatt.c b/android/gatt.c
index 2ad5936..9f3a796 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;
@@ -3900,6 +3902,35 @@ 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, void *user_data)
+{
+ struct req_data *data = req_data;
+
+ /* Set services changed notification flag */
+ data->dev->notify_services_changed = !!(*val);
+}
+
+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;
@@ -3968,6 +3999,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
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 73f5119..314e09b 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -33,7 +33,8 @@ bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);
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,
+typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset,
+ const uint8_t *value,
size_t len, void *req_data,
void *user_data);
--
1.8.4
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
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 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/android/gatt.c b/android/gatt.c
index d2b1684..2ad5936 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3811,6 +3811,95 @@ static void register_gap_service(void)
gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
}
+
+/* TODO: Figure out the best way for this to be not hard coded. */
+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, void *user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct req_data *data = req_data;
+ const char *buf = user_data;
+ uint16_t len;
+
+ len = enc_read_resp((uint8_t *)buf, strlen(buf), pdu, sizeof(pdu));
+
+ g_attrib_send(data->dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+}
+
+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);
+
+ 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;
@@ -3878,6 +3967,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
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 5ca22e2..d2b1684 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -161,6 +161,8 @@ static struct gatt_db *gatt_db = NULL;
static GIOChannel *listening_sk = NULL;
+static char device_name[249] = "BlueZ for Android";
+
static void bt_le_discovery_stop_cb(void);
static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
@@ -3717,6 +3719,98 @@ 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,
+ void *user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct req_data *data = req_data;
+ uint16_t len;
+
+ DBG("");
+
+ if (handle == gap_srvc_data.dev_name) {
+ len = enc_read_resp((uint8_t *)&device_name[0],
+ strlen(device_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:
+ g_attrib_send(data->dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+}
+
+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;
@@ -3783,6 +3877,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
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
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
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 d87af39..5ca22e2 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)
+ g_attrib_unregister(device->attrib, device->server_id);
+
if (device->attrib) {
GAttrib *attrib = device->attrib;
device->attrib = NULL;
@@ -954,6 +958,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;
@@ -989,6 +995,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)
+ error("gatt: Could not attache to server");
+
device_set_state(dev, DEVICE_CONNECTED);
status = GATT_SUCCESS;
@@ -3594,6 +3606,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;
@@ -3659,7 +3710,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
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 46128af..d20b87a 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3320,30 +3320,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
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 d20b87a..d87af39 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -158,6 +158,8 @@ static int32_t app_id = 1;
static struct queue *listen_clients = 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)
@@ -3592,6 +3594,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 = NULL;
+ 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_clients, 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 == NULL) {
+ 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("");
@@ -3631,6 +3725,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
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 f34e786..adee9d0 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3351,26 +3351,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
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 adee9d0..46128af 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3390,25 +3390,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
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 014458e..2885c51 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3237,25 +3237,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
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 2885c51..f34e786 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3276,31 +3276,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
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 ed4ee75..0277c02 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"
@@ -155,6 +156,7 @@ static struct queue *app_connections = NULL;
static int32_t app_id = 1;
static struct queue *listen_clients = NULL;
+static struct gatt_db *gatt_db = NULL;
static void bt_le_discovery_stop_cb(void);
@@ -3193,32 +3195,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)
@@ -3521,9 +3534,10 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
gatt_apps = queue_new();
app_connections = queue_new();
listen_clients = queue_new();
+ gatt_db = gatt_db_new();
if (!gatt_devices || !gatt_apps || !listen_clients ||
- !app_connections) {
+ !app_connections || !gatt_db) {
error("gatt: Failed to allocate memory for queues");
queue_destroy(gatt_apps, NULL);
@@ -3538,6 +3552,9 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
queue_destroy(listen_clients, NULL);
listen_clients = NULL;
+ gatt_db_destroy(gatt_db);
+ gatt_db = NULL;
+
return false;
}
@@ -3569,4 +3586,7 @@ void bt_gatt_unregister(void)
queue_destroy(listen_clients, NULL);
listen_clients = NULL;
+
+ gatt_db_destroy(gatt_db);
+ gatt_db = NULL;
}
--
1.8.4
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 0277c02..014458e 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3379,27 +3379,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
From: Jakub Tyszkowski <[email protected]>
---
android/gatt.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/android/gatt.c b/android/gatt.c
index 0e81e7d..ed4ee75 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -599,6 +599,11 @@ failed:
static void send_client_disconnection_notify(struct app_connection *connection,
int32_t status)
{
+ /* disconnect and connect events are binary the same so its safe to pass
+ * hal_ev_gatt_client_disconnect struct even for connect event.
+ * Note that server do this the right way, having single event for
+ * connection state notification.
+ */
struct hal_ev_gatt_client_disconnect ev;
ev.client_if = connection->app->id;
--
1.8.4
With this patch Android app can send indication to remote device
---
android/gatt.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)
diff --git a/android/gatt.c b/android/gatt.c
index 3e291cf..74d36bb 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3666,10 +3666,38 @@ 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;
+ 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;
+ }
+
+ 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));
+
+ g_attrib_send(conn->device->attrib , 0, pdu, length, NULL, NULL , NULL);
+
+ 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