* Re: [PATCH] Fix invalid reference to GATT service structure
From: Johan Hedberg @ 2010-11-05 5:03 UTC (permalink / raw)
To: Claudio Takahasi; +Cc: linux-bluetooth
In-Reply-To: <1288899810-18722-3-git-send-email-claudio.takahasi@openbossa.org>
Hi Claudio,
On Thu, Nov 04, 2010, Claudio Takahasi wrote:
> ---
> attrib/client.c | 9 ++++-----
> 1 files changed, 4 insertions(+), 5 deletions(-)
This one has also been pushed upstream. Thanks.
Johan
^ permalink raw reply
* Re: [PATCH] Enable GATT over LE link on the attribute client
From: Johan Hedberg @ 2010-11-05 5:03 UTC (permalink / raw)
To: Claudio Takahasi; +Cc: linux-bluetooth
In-Reply-To: <1288899810-18722-2-git-send-email-claudio.takahasi@openbossa.org>
Hi Claudio,
On Thu, Nov 04, 2010, Claudio Takahasi wrote:
> ---
> attrib/client.c | 22 ++++++++++++----------
> attrib/gatt.h | 2 ++
> attrib/gatttool.c | 1 -
> 3 files changed, 14 insertions(+), 11 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
* Re: [PATCH] Move set_nonblocking function to audio/unix.c
From: Johan Hedberg @ 2010-11-05 5:02 UTC (permalink / raw)
To: Claudio Takahasi; +Cc: linux-bluetooth
In-Reply-To: <1288899810-18722-1-git-send-email-claudio.takahasi@openbossa.org>
Hi Claudio,
On Thu, Nov 04, 2010, Claudio Takahasi wrote:
> ---
> audio/unix.c | 20 ++++++++++++++++++++
> src/glib-helper.c | 20 --------------------
> src/glib-helper.h | 2 --
> 3 files changed, 20 insertions(+), 22 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
* [PATCH] Move gattrib source files to src directory
From: Claudio Takahasi @ 2010-11-04 20:07 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
gattrib related functions will be required during the device creation
for GATT enabled devices(BR/EDR and LE). Primary service discovery is
a pre-condition to probe the GATT device driver.
---
Makefile.am | 7 +-
attrib/att.c | 764 ------------------------------------------------------
attrib/att.h | 206 ---------------
attrib/gatt.c | 113 --------
attrib/gatt.h | 43 ---
attrib/gattrib.c | 535 --------------------------------------
attrib/gattrib.h | 72 -----
src/att.c | 764 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/att.h | 206 +++++++++++++++
src/gatt.c | 113 ++++++++
src/gatt.h | 43 +++
src/gattrib.c | 535 ++++++++++++++++++++++++++++++++++++++
src/gattrib.h | 72 +++++
13 files changed, 1736 insertions(+), 1737 deletions(-)
delete mode 100644 attrib/att.c
delete mode 100644 attrib/att.h
delete mode 100644 attrib/gatt.c
delete mode 100644 attrib/gatt.h
delete mode 100644 attrib/gattrib.c
delete mode 100644 attrib/gattrib.h
create mode 100644 src/att.c
create mode 100644 src/att.h
create mode 100644 src/gatt.c
create mode 100644 src/gatt.h
create mode 100644 src/gattrib.c
create mode 100644 src/gattrib.h
diff --git a/Makefile.am b/Makefile.am
index 873f2df..47e2740 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -87,8 +87,8 @@ sbc_sbctest_CFLAGS = @SNDFILE_CFLAGS@
endif
endif
-attrib_sources = attrib/att.h attrib/att.c attrib/gatt.h attrib/gatt.c \
- attrib/gattrib.h attrib/gattrib.c
+attrib_sources = src/att.h src/att.c src/gatt.h src/gatt.c \
+ src/gattrib.h src/gattrib.c
gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
gdbus/object.c gdbus/polkit.c
@@ -176,8 +176,7 @@ endif
if ATTRIBPLUGIN
bin_PROGRAMS += attrib/gatttool
-attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
- attrib/gattrib.c btio/btio.c
+attrib_gatttool_SOURCES = attrib/gatttool.c $(attrib_sources) btio/btio.c
attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@
builtin_modules += attrib
diff --git a/attrib/att.c b/attrib/att.c
deleted file mode 100644
index fe41d0e..0000000
--- a/attrib/att.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include "att.h"
-
-const char *att_ecode2str(uint8_t status)
-{
- switch (status) {
- case ATT_ECODE_INVALID_HANDLE:
- return "Invalid handle";
- case ATT_ECODE_READ_NOT_PERM:
- return "Atribute can't be read";
- case ATT_ECODE_WRITE_NOT_PERM:
- return "Attribute can't be written";
- case ATT_ECODE_INVALID_PDU:
- return "Attribute PDU was invalid";
- case ATT_ECODE_INSUFF_AUTHEN:
- return "Attribute requires authentication before read/write";
- case ATT_ECODE_REQ_NOT_SUPP:
- return "Server doesn't support the request received";
- case ATT_ECODE_INVALID_OFFSET:
- return "Offset past the end of the attribute";
- case ATT_ECODE_INSUFF_AUTHO:
- return "Attribute requires authorization before read/write";
- case ATT_ECODE_PREP_QUEUE_FULL:
- return "Too many prepare writes have been queued";
- case ATT_ECODE_ATTR_NOT_FOUND:
- return "No attribute found within the given range";
- case ATT_ECODE_ATTR_NOT_LONG:
- return "Attribute can't be read/written using Read Blob Req";
- case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
- return "Encryption Key Size is insufficient";
- case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
- return "Attribute value length is invalid";
- case ATT_ECODE_UNLIKELY:
- return "Request attribute has encountered an unlikely error";
- case ATT_ECODE_INSUFF_ENC:
- return "Encryption required before read/write";
- case ATT_ECODE_UNSUPP_GRP_TYPE:
- return "Attribute type is not a supported grouping attribute";
- case ATT_ECODE_INSUFF_RESOURCES:
- return "Insufficient Resources to complete the request";
- case ATT_ECODE_IO:
- return "Internal application error: I/O";
- default:
- return "Unexpected error code";
- }
-}
-
-void att_data_list_free(struct att_data_list *list)
-{
- int i;
-
- for (i = 0; i < list->num; i++)
- free(list->data[i]);
-
- free(list->data);
- free(list);
-}
-
-uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
- uint16_t length;
-
- if (!uuid)
- return 0;
-
- if (uuid->type == SDP_UUID16)
- length = 2;
- else if (uuid->type == SDP_UUID128)
- length = 16;
- else
- return 0;
-
- if (len < min_len + length)
- return 0;
-
- pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
- att_put_u16(start, &pdu[1]);
- att_put_u16(end, &pdu[3]);
-
- if (uuid->type == SDP_UUID16)
- att_put_u16(uuid->value.uuid16, &pdu[5]);
- else
- memcpy(&pdu[5], &uuid->value.uuid128, length);
-
- return min_len + length;
-}
-
-uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end, uuid_t *uuid)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
-
- if (pdu == NULL)
- return 0;
-
- if (start == NULL || end == NULL || uuid == NULL)
- return 0;
-
- if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
- return 0;
-
- if (len < min_len + 2)
- return 0;
-
- *start = att_get_u16(&pdu[1]);
- *end = att_get_u16(&pdu[3]);
- if (len == min_len + 2)
- sdp_uuid16_create(uuid, att_get_u16(&pdu[5]));
- else
- sdp_uuid128_create(uuid, &pdu[5]);
-
- return len;
-}
-
-uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
- int len)
-{
- int i;
- uint16_t w;
- uint8_t *ptr;
-
- if (list == NULL)
- return 0;
-
- if (len < list->len + 2)
- return 0;
-
- pdu[0] = ATT_OP_READ_BY_GROUP_RESP;
- pdu[1] = list->len;
-
- ptr = &pdu[2];
-
- for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
- memcpy(ptr, list->data[i], list->len);
- ptr += list->len;
- w += list->len;
- }
-
- return w;
-}
-
-struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len)
-{
- struct att_data_list *list;
- const uint8_t *ptr;
- int i;
-
- if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
- return NULL;
-
- list = malloc(sizeof(struct att_data_list));
- list->len = pdu[1];
- list->num = (len - 2) / list->len;
-
- list->data = malloc(sizeof(uint8_t *) * list->num);
- ptr = &pdu[2];
-
- for (i = 0; i < list->num; i++) {
- list->data[i] = malloc(sizeof(uint8_t) * list->len);
- memcpy(list->data[i], ptr, list->len);
- ptr += list->len;
- }
-
- return list;
-}
-
-uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len)
-{
- return 0;
-}
-
-uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
- uint16_t length;
-
- if (!uuid)
- return 0;
-
- if (uuid->type == SDP_UUID16)
- length = 2;
- else if (uuid->type == SDP_UUID128)
- length = 16;
- else
- return 0;
-
- if (len < min_len + length)
- return 0;
-
- pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
- att_put_u16(start, &pdu[1]);
- att_put_u16(end, &pdu[3]);
-
- if (uuid->type == SDP_UUID16)
- att_put_u16(uuid->value.uuid16, &pdu[5]);
- else
- memcpy(&pdu[5], &uuid->value.uuid128, length);
-
- return min_len + length;
-}
-
-uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end, uuid_t *uuid)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
-
- if (pdu == NULL)
- return 0;
-
- if (start == NULL || end == NULL || uuid == NULL)
- return 0;
-
- if (len < min_len + 2)
- return 0;
-
- if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
- return 0;
-
- *start = att_get_u16(&pdu[1]);
- *end = att_get_u16(&pdu[3]);
-
- if (len == min_len + 2)
- sdp_uuid16_create(uuid, att_get_u16(&pdu[5]));
- else
- sdp_uuid128_create(uuid, &pdu[5]);
-
- return len;
-}
-
-uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, int len)
-{
- uint8_t *ptr;
- int i, w;
-
- if (list == NULL)
- return 0;
-
- if (pdu == NULL)
- return 0;
-
- if (len < list->len + 2)
- return 0;
-
- pdu[0] = ATT_OP_READ_BY_TYPE_RESP;
- pdu[1] = list->len;
- ptr = &pdu[2];
-
- for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
- memcpy(ptr, list->data[i], list->len);
- ptr += list->len;
- w += list->len;
- }
-
- return w;
-}
-
-struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len)
-{
- struct att_data_list *list;
- const uint8_t *ptr;
- int i;
-
- if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
- return NULL;
-
- list = malloc(sizeof(struct att_data_list));
- list->len = pdu[1];
- list->num = (len - 2) / list->len;
-
- list->data = malloc(sizeof(uint8_t *) * list->num);
- ptr = &pdu[2];
-
- for (i = 0; i < list->num; i++) {
- list->data[i] = malloc(sizeof(uint8_t) * list->len);
- memcpy(list->data[i], ptr, list->len);
- ptr += list->len;
- }
-
- return list;
-}
-
-uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
- uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (vlen > len - min_len)
- vlen = len - min_len;
-
- pdu[0] = ATT_OP_WRITE_CMD;
- att_put_u16(handle, &pdu[1]);
-
- if (vlen > 0) {
- memcpy(&pdu[3], value, vlen);
- return min_len + vlen;
- }
-
- return min_len;
-}
-
-uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
- uint8_t *value, int *vlen)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
-
- if (pdu == NULL)
- return 0;
-
- if (value == NULL || vlen == NULL || handle == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (pdu[0] != ATT_OP_WRITE_CMD)
- return 0;
-
- *handle = att_get_u16(&pdu[1]);
- memcpy(value, pdu + min_len, len - min_len);
- *vlen = len - min_len;
-
- return len;
-}
-
-uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
- uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (vlen > len - min_len)
- vlen = len - min_len;
-
- pdu[0] = ATT_OP_WRITE_REQ;
- att_put_u16(handle, &pdu[1]);
-
- if (vlen > 0) {
- memcpy(&pdu[3], value, vlen);
- return min_len + vlen;
- }
-
- return min_len;
-}
-
-uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
- uint8_t *value, int *vlen)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
-
- if (pdu == NULL)
- return 0;
-
- if (value == NULL || vlen == NULL || handle == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (pdu[0] != ATT_OP_WRITE_REQ)
- return 0;
-
- *handle = att_get_u16(&pdu[1]);
- *vlen = len - min_len;
- if (*vlen > 0)
- memcpy(value, pdu + min_len, *vlen);
-
- return len;
-}
-
-uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- pdu[0] = ATT_OP_READ_REQ;
- att_put_u16(handle, &pdu[1]);
-
- return min_len;
-}
-
-uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
-
- if (pdu == NULL)
- return 0;
-
- if (handle == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (pdu[0] != ATT_OP_READ_REQ)
- return 0;
-
- *handle = att_get_u16(&pdu[1]);
-
- return min_len;
-}
-
-uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
-{
- if (pdu == NULL)
- return 0;
-
- /* If the attribute value length is longer than the allowed PDU size,
- * send only the octets that fit on the PDU. The remaining octets can
- * be requested using the Read Blob Request. */
- if (vlen > len - 1)
- vlen = len - 1;
-
- pdu[0] = ATT_OP_READ_RESP;
-
- memcpy(pdu + 1, value, vlen);
-
- return vlen + 1;
-}
-
-uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen)
-{
- if (pdu == NULL)
- return 0;
-
- if (value == NULL || vlen == NULL)
- return 0;
-
- if (pdu[0] != ATT_OP_READ_RESP)
- return 0;
-
- memcpy(value, pdu + 1, len - 1);
-
- *vlen = len - 1;
-
- return len;
-}
-
-uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
- uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
- sizeof(handle) + sizeof(status);
- uint16_t u16;
-
- if (len < min_len)
- return 0;
-
- u16 = htobs(handle);
- pdu[0] = ATT_OP_ERROR;
- pdu[1] = opcode;
- memcpy(&pdu[2], &u16, sizeof(u16));
- pdu[4] = status;
-
- return min_len;
-}
-
-uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- pdu[0] = ATT_OP_FIND_INFO_REQ;
- att_put_u16(start, &pdu[1]);
- att_put_u16(end, &pdu[3]);
-
- return min_len;
-}
-
-uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (start == NULL || end == NULL)
- return 0;
-
- if (pdu[0] != ATT_OP_FIND_INFO_REQ)
- return 0;
-
- *start = att_get_u16(&pdu[1]);
- *end = att_get_u16(&pdu[3]);
-
- return min_len;
-}
-
-uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
- uint8_t *pdu, int len)
-{
- uint8_t *ptr;
- int i, w;
-
- if (pdu == NULL)
- return 0;
-
- if (list == NULL)
- return 0;
-
- if (len < list->len + 2)
- return 0;
-
- pdu[0] = ATT_OP_FIND_INFO_RESP;
- pdu[1] = format;
- ptr = (void *) &pdu[2];
-
- for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
- memcpy(ptr, list->data[i], list->len);
- ptr += list->len;
- w += list->len;
- }
-
- return w;
-}
-
-struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
- uint8_t *format)
-{
- struct att_data_list *list;
- uint8_t *ptr;
- int i;
-
- if (pdu == NULL)
- return 0;
-
- if (format == NULL)
- return 0;
-
- if (pdu[0] != ATT_OP_FIND_INFO_RESP)
- return 0;
-
- *format = pdu[1];
-
- list = malloc(sizeof(struct att_data_list));
-
- list->len = sizeof(pdu[0]) + sizeof(*format);
- if (*format == 0x01)
- list->len += 2;
- else if (*format == 0x02)
- list->len += 16;
-
- list->num = (len - 2) / list->len;
- list->data = malloc(sizeof(uint8_t *) * list->num);
-
- ptr = (void *) &pdu[2];
-
- for (i = 0; i < list->num; i++) {
- list->data[i] = malloc(list->len);
- memcpy(list->data[i], ptr, list->len);
- ptr += list->len;
- }
-
- return list;
-}
-
-uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
-
- if (pdu == NULL)
- return 0;
-
- if (len < (a->len + min_len))
- return 0;
-
- pdu[0] = ATT_OP_HANDLE_NOTIFY;
- att_put_u16(a->handle, &pdu[1]);
- memcpy(&pdu[3], a->data, a->len);
-
- return a->len + min_len;
-}
-
-uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
-
- if (pdu == NULL)
- return 0;
-
- if (len < (a->len + min_len))
- return 0;
-
- pdu[0] = ATT_OP_HANDLE_IND;
- att_put_u16(a->handle, &pdu[1]);
- memcpy(&pdu[3], a->data, a->len);
-
- return a->len + min_len;
-}
-
-struct attribute *dec_indication(const uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
-
- struct attribute *a;
-
- if (pdu == NULL)
- return NULL;
-
- if (pdu[0] != ATT_OP_HANDLE_IND)
- return NULL;
-
- if (len < min_len)
- return NULL;
-
- a = malloc(sizeof(struct attribute) + len - min_len);
- if (a == NULL)
- return NULL;
-
- a->len = len - min_len;
-
- a->handle = att_get_u16(&pdu[1]);
- memcpy(a->data, &pdu[3], a->len);
-
- return a;
-}
-
-uint16_t enc_confirmation(uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- pdu[0] = ATT_OP_HANDLE_CNF;
-
- return min_len;
-}
-
-uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- pdu[0] = ATT_OP_MTU_REQ;
- att_put_u16(mtu, &pdu[1]);
-
- return min_len;
-}
-
-uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
-
- if (pdu == NULL)
- return 0;
-
- if (mtu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (pdu[0] != ATT_OP_MTU_REQ)
- return 0;
-
- *mtu = att_get_u16(&pdu[1]);
-
- return min_len;
-}
-
-uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
- if (pdu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- pdu[0] = ATT_OP_MTU_RESP;
- att_put_u16(mtu, &pdu[1]);
-
- return min_len;
-}
-
-uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu)
-{
- const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
-
- if (pdu == NULL)
- return 0;
-
- if (mtu == NULL)
- return 0;
-
- if (len < min_len)
- return 0;
-
- if (pdu[0] != ATT_OP_MTU_RESP)
- return 0;
-
- *mtu = att_get_u16(&pdu[1]);
-
- return min_len;
-}
diff --git a/attrib/att.h b/attrib/att.h
deleted file mode 100644
index ea49dc2..0000000
--- a/attrib/att.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-/* GATT Profile Attribute types */
-#define GATT_PRIM_SVC_UUID 0x2800
-#define GATT_SND_SVC_UUID 0x2801
-#define GATT_INCLUDE_UUID 0x2802
-#define GATT_CHARAC_UUID 0x2803
-
-/* GATT Characteristic Types */
-#define GATT_CHARAC_DEVICE_NAME 0x2A00
-#define GATT_CHARAC_APPEARANCE 0x2A01
-#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
-#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
-#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
-#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
-
-/* GATT Characteristic Descriptors */
-#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
-#define GATT_CHARAC_USER_DESC_UUID 0x2901
-#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
-#define GATT_SERVER_CHARAC_CFG_UUID 0x2903
-#define GATT_CHARAC_FMT_UUID 0x2904
-#define GATT_CHARAC_AGREG_FMT_UUID 0x2905
-
-/* Attribute Protocol Opcodes */
-#define ATT_OP_ERROR 0x01
-#define ATT_OP_MTU_REQ 0x02
-#define ATT_OP_MTU_RESP 0x03
-#define ATT_OP_FIND_INFO_REQ 0x04
-#define ATT_OP_FIND_INFO_RESP 0x05
-#define ATT_OP_FIND_BY_TYPE_REQ 0x06
-#define ATT_OP_FIND_BY_TYPE_RESP 0x07
-#define ATT_OP_READ_BY_TYPE_REQ 0x08
-#define ATT_OP_READ_BY_TYPE_RESP 0x09
-#define ATT_OP_READ_REQ 0x0A
-#define ATT_OP_READ_RESP 0x0B
-#define ATT_OP_READ_BLOB_REQ 0x0C
-#define ATT_OP_READ_BLOB_RESP 0x0D
-#define ATT_OP_READ_MULTI_REQ 0x0E
-#define ATT_OP_READ_MULTI_RESP 0x0F
-#define ATT_OP_READ_BY_GROUP_REQ 0x10
-#define ATT_OP_READ_BY_GROUP_RESP 0x11
-#define ATT_OP_WRITE_REQ 0x12
-#define ATT_OP_WRITE_RESP 0x13
-#define ATT_OP_WRITE_CMD 0x52
-#define ATT_OP_PREP_WRITE_REQ 0x16
-#define ATT_OP_PREP_WRITE_RESP 0x17
-#define ATT_OP_EXEC_WRITE_REQ 0x18
-#define ATT_OP_EXEC_WRITE_RESP 0x19
-#define ATT_OP_HANDLE_NOTIFY 0x1B
-#define ATT_OP_HANDLE_IND 0x1D
-#define ATT_OP_HANDLE_CNF 0x1E
-#define ATT_OP_SIGNED_WRITE_CMD 0xD2
-
-/* Error codes for Error response PDU */
-#define ATT_ECODE_INVALID_HANDLE 0x01
-#define ATT_ECODE_READ_NOT_PERM 0x02
-#define ATT_ECODE_WRITE_NOT_PERM 0x03
-#define ATT_ECODE_INVALID_PDU 0x04
-#define ATT_ECODE_INSUFF_AUTHEN 0x05
-#define ATT_ECODE_REQ_NOT_SUPP 0x06
-#define ATT_ECODE_INVALID_OFFSET 0x07
-#define ATT_ECODE_INSUFF_AUTHO 0x08
-#define ATT_ECODE_PREP_QUEUE_FULL 0x09
-#define ATT_ECODE_ATTR_NOT_FOUND 0x0A
-#define ATT_ECODE_ATTR_NOT_LONG 0x0B
-#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0C
-#define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0D
-#define ATT_ECODE_UNLIKELY 0x0E
-#define ATT_ECODE_INSUFF_ENC 0x0F
-#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10
-#define ATT_ECODE_INSUFF_RESOURCES 0x11
-/* Application error */
-#define ATT_ECODE_IO 0xFF
-
-/* Characteristic Property bit field */
-#define ATT_CHAR_PROPER_BROADCAST 0x01
-#define ATT_CHAR_PROPER_READ 0x02
-#define ATT_CHAR_PROPER_WRITE_WITHOUT_RESP 0x04
-#define ATT_CHAR_PROPER_WRITE 0x08
-#define ATT_CHAR_PROPER_NOTIFY 0x10
-#define ATT_CHAR_PROPER_INDICATE 0x20
-#define ATT_CHAR_PROPER_AUTH 0x40
-#define ATT_CHAR_PROPER_EXT_PROPER 0x80
-
-
-#define ATT_MAX_MTU 256
-#define ATT_DEFAULT_MTU 23
-
-struct attribute {
- uint16_t handle;
- uuid_t uuid;
- int len;
- uint8_t data[0];
-};
-
-struct att_data_list {
- uint16_t num;
- uint16_t len;
- uint8_t **data;
-};
-
-/* These functions do byte conversion */
-static inline uint8_t att_get_u8(const void *ptr)
-{
- const uint8_t *u8_ptr = ptr;
- return bt_get_unaligned(u8_ptr);
-}
-
-static inline uint16_t att_get_u16(const void *ptr)
-{
- const uint16_t *u16_ptr = ptr;
- return btohs(bt_get_unaligned(u16_ptr));
-}
-
-static inline uint32_t att_get_u32(const void *ptr)
-{
- const uint32_t *u32_ptr = ptr;
- return btohl(bt_get_unaligned(u32_ptr));
-}
-
-static inline void att_put_u8(uint8_t src, void *dst)
-{
- bt_put_unaligned(src, (uint8_t *) dst);
-}
-
-static inline void att_put_u16(uint16_t src, void *dst)
-{
- bt_put_unaligned(htobs(src), (uint16_t *) dst);
-}
-
-static inline void att_put_u32(uint16_t src, void *dst)
-{
- bt_put_unaligned(htobl(src), (uint32_t *) dst);
-}
-
-void att_data_list_free(struct att_data_list *list);
-
-const char *att_ecode2str(uint8_t status);
-uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len);
-uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end, uuid_t *uuid);
-uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, int len);
-uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len);
-struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len);
-uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
- uint8_t *pdu, int len);
-uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end, uuid_t *uuid);
-uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
- int len);
-uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
- uint8_t *pdu, int len);
-uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
- uint8_t *value, int *vlen);
-struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len);
-uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
- uint8_t *pdu, int len);
-uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
- uint8_t *value, int *vlen);
-uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len);
-uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle);
-uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len);
-uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen);
-uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
- uint8_t *pdu, int len);
-uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len);
-uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
- uint16_t *end);
-uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
- uint8_t *pdu, int len);
-struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
- uint8_t *format);
-uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len);
-uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len);
-struct attribute *dec_indication(const uint8_t *pdu, int len);
-uint16_t enc_confirmation(uint8_t *pdu, int len);
-
-uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len);
-uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu);
-uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len);
-uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu);
diff --git a/attrib/gatt.c b/attrib/gatt.c
deleted file mode 100644
index 24ec990..0000000
--- a/attrib/gatt.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <stdint.h>
-#include <glib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include "att.h"
-#include "gattrib.h"
-#include "gatt.h"
-
-guint gatt_discover_primary(GAttrib *attrib, uint16_t start,
- uint16_t end, GAttribResultFunc func, gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- uuid_t uuid;
- guint16 plen;
-
- sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
-
- plen = enc_read_by_grp_req(start, end, &uuid, pdu, sizeof(pdu));
- if (plen == 0)
- return 0;
-
- return g_attrib_send(attrib, ATT_OP_READ_BY_GROUP_REQ,
- pdu, plen, func, user_data, NULL);
-}
-
-guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
- GAttribResultFunc func, gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- uuid_t uuid;
- guint16 plen;
-
- sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
-
- plen = enc_read_by_type_req(start, end, &uuid, pdu, sizeof(pdu));
- if (plen == 0)
- return 0;
-
- return g_attrib_send(attrib, ATT_OP_READ_BY_TYPE_REQ,
- pdu, plen, func, user_data, NULL);
-}
-
-guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
- gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- guint16 plen;
-
- plen = enc_read_req(handle, pdu, sizeof(pdu));
- return g_attrib_send(attrib, ATT_OP_READ_REQ, pdu, plen, func,
- user_data, NULL);
-}
-
-guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
- int vlen, GAttribResultFunc func, gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- guint16 plen;
-
- plen = enc_write_req(handle, value, vlen, pdu, sizeof(pdu));
- return g_attrib_send(attrib, ATT_OP_WRITE_REQ, pdu, plen, func,
- user_data, NULL);
-}
-
-guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
- GAttribResultFunc func, gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- guint16 plen;
-
- plen = enc_find_info_req(start, end, pdu, sizeof(pdu));
- if (plen == 0)
- return 0;
-
- return g_attrib_send(attrib, ATT_OP_FIND_INFO_REQ, pdu, plen, func,
- user_data, NULL);
-}
-
-guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
- GDestroyNotify notify, gpointer user_data)
-{
- uint8_t pdu[ATT_DEFAULT_MTU];
- guint16 plen;
-
- plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
- return g_attrib_send(attrib, ATT_OP_WRITE_CMD, pdu, plen, NULL,
- user_data, notify);
-}
diff --git a/attrib/gatt.h b/attrib/gatt.h
deleted file mode 100644
index f1599c2..0000000
--- a/attrib/gatt.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#define GATT_CID 4
-
-guint gatt_discover_primary(GAttrib *attrib, uint16_t start,
- uint16_t end, GAttribResultFunc func, gpointer user_data);
-
-guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
- GAttribResultFunc func, gpointer user_data);
-
-guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
- gpointer user_data);
-
-guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
- int vlen, GAttribResultFunc func, gpointer user_data);
-
-guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
- GAttribResultFunc func, gpointer user_data);
-
-guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
- GDestroyNotify notify, gpointer user_data);
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
deleted file mode 100644
index ed18168..0000000
--- a/attrib/gattrib.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <stdint.h>
-#include <string.h>
-#include <glib.h>
-
-#include <stdio.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
-#include "att.h"
-#include "gattrib.h"
-
-struct _GAttrib {
- GIOChannel *io;
- gint refs;
- gint mtu;
- guint read_watch;
- guint write_watch;
- GQueue *queue;
- GSList *events;
- guint next_cmd_id;
- guint next_evt_id;
- GDestroyNotify destroy;
- GAttribDisconnectFunc disconnect;
- gpointer destroy_user_data;
- gpointer disc_user_data;
-};
-
-struct command {
- guint id;
- guint8 opcode;
- guint8 *pdu;
- guint16 len;
- guint8 expected;
- gboolean sent;
- GAttribResultFunc func;
- gpointer user_data;
- GDestroyNotify notify;
-};
-
-struct event {
- guint id;
- guint8 expected;
- GAttribNotifyFunc func;
- gpointer user_data;
- GDestroyNotify notify;
-};
-
-static guint8 opcode2expected(guint8 opcode)
-{
- switch (opcode) {
- case ATT_OP_MTU_REQ:
- return ATT_OP_MTU_RESP;
-
- case ATT_OP_FIND_INFO_REQ:
- return ATT_OP_FIND_INFO_RESP;
-
- case ATT_OP_FIND_BY_TYPE_REQ:
- return ATT_OP_FIND_BY_TYPE_RESP;
-
- case ATT_OP_READ_BY_TYPE_REQ:
- return ATT_OP_READ_BY_TYPE_RESP;
-
- case ATT_OP_READ_REQ:
- return ATT_OP_READ_RESP;
-
- case ATT_OP_READ_BLOB_REQ:
- return ATT_OP_READ_BLOB_RESP;
-
- case ATT_OP_READ_MULTI_REQ:
- return ATT_OP_READ_MULTI_RESP;
-
- case ATT_OP_READ_BY_GROUP_REQ:
- return ATT_OP_READ_BY_GROUP_RESP;
-
- case ATT_OP_WRITE_REQ:
- return ATT_OP_WRITE_RESP;
-
- case ATT_OP_PREP_WRITE_REQ:
- return ATT_OP_PREP_WRITE_RESP;
-
- case ATT_OP_EXEC_WRITE_REQ:
- return ATT_OP_EXEC_WRITE_RESP;
-
- case ATT_OP_HANDLE_IND:
- return ATT_OP_HANDLE_CNF;
- }
-
- return 0;
-}
-
-static gboolean is_response(guint8 opcode)
-{
- switch (opcode) {
- case ATT_OP_ERROR:
- case ATT_OP_MTU_RESP:
- case ATT_OP_FIND_INFO_RESP:
- case ATT_OP_FIND_BY_TYPE_RESP:
- case ATT_OP_READ_BY_TYPE_RESP:
- case ATT_OP_READ_RESP:
- case ATT_OP_READ_BLOB_RESP:
- case ATT_OP_READ_MULTI_RESP:
- case ATT_OP_READ_BY_GROUP_RESP:
- case ATT_OP_WRITE_RESP:
- case ATT_OP_PREP_WRITE_RESP:
- case ATT_OP_EXEC_WRITE_RESP:
- case ATT_OP_HANDLE_CNF:
- return TRUE;
- }
-
- return FALSE;
-}
-
-GAttrib *g_attrib_ref(GAttrib *attrib)
-{
- if (!attrib)
- return NULL;
-
- g_atomic_int_inc(&attrib->refs);
-
- return attrib;
-}
-
-static void command_destroy(struct command *cmd)
-{
- if (cmd->notify)
- cmd->notify(cmd->user_data);
-
- g_free(cmd->pdu);
- g_free(cmd);
-}
-
-static void event_destroy(struct event *evt)
-{
- if (evt->notify)
- evt->notify(evt->user_data);
-
- g_free(evt);
-}
-
-void g_attrib_unref(GAttrib *attrib)
-{
- GSList *l;
- struct command *c;
-
- if (!attrib)
- return;
-
- if (g_atomic_int_dec_and_test(&attrib->refs) == FALSE)
- return;
-
- while ((c = g_queue_pop_head(attrib->queue)))
- command_destroy(c);
-
- attrib->queue = NULL;
-
- for (l = attrib->events; l; l = l->next)
- event_destroy(l->data);
-
- g_slist_free(attrib->events);
- attrib->events = NULL;
-
- if (attrib->write_watch > 0)
- g_source_remove(attrib->write_watch);
-
- if (attrib->read_watch > 0) {
- g_source_remove(attrib->read_watch);
- g_io_channel_unref(attrib->io);
- }
-
-
- if (attrib->destroy)
- attrib->destroy(attrib->destroy_user_data);
-
- g_free(attrib);
-}
-
-gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
- GAttribDisconnectFunc disconnect, gpointer user_data)
-{
- if (attrib == NULL)
- return FALSE;
-
- attrib->disconnect = disconnect;
- attrib->disc_user_data = user_data;
-
- return TRUE;
-}
-
-gboolean g_attrib_set_destroy_function(GAttrib *attrib,
- GDestroyNotify destroy, gpointer user_data)
-{
- if (attrib == NULL)
- return FALSE;
-
- attrib->destroy = destroy;
- attrib->destroy_user_data = user_data;
-
- return TRUE;
-}
-
-static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
- gpointer data)
-{
- struct _GAttrib *attrib = data;
- struct command *cmd;
- GError *gerr = NULL;
- gsize len;
- GIOStatus iostat;
-
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- if (attrib->disconnect)
- attrib->disconnect(attrib->disc_user_data);
-
- return FALSE;
- }
-
- cmd = g_queue_peek_head(attrib->queue);
- if (cmd == NULL)
- return FALSE;
-
- iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
- &len, &gerr);
- if (iostat != G_IO_STATUS_NORMAL)
- return FALSE;
-
- g_io_channel_flush(io, NULL);
-
- if (cmd->expected == 0) {
- g_queue_pop_head(attrib->queue);
- command_destroy(cmd);
-
- return TRUE;
- }
-
- cmd->sent = TRUE;
-
- return FALSE;
-}
-
-static void destroy_sender(gpointer data)
-{
- struct _GAttrib *attrib = data;
-
- attrib->write_watch = 0;
-}
-
-static void wake_up_sender(struct _GAttrib *attrib)
-{
- if (attrib->write_watch == 0)
- attrib->write_watch = g_io_add_watch_full(attrib->io,
- G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data,
- attrib, destroy_sender);
-}
-
-static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
-{
- struct _GAttrib *attrib = data;
- struct command *cmd = NULL;
- GSList *l;
- uint8_t buf[512], status;
- gsize len;
- GIOStatus iostat;
-
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- attrib->read_watch = 0;
- if (attrib->disconnect)
- attrib->disconnect(attrib->disc_user_data);
- return FALSE;
- }
-
- memset(buf, 0, sizeof(buf));
-
- iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf),
- &len, NULL);
- if (iostat != G_IO_STATUS_NORMAL) {
- status = ATT_ECODE_IO;
- goto done;
- }
-
- for (l = attrib->events; l; l = l->next) {
- struct event *evt = l->data;
-
- if (evt->expected == buf[0] ||
- evt->expected == GATTRIB_ALL_EVENTS)
- evt->func(buf, len, evt->user_data);
- }
-
- if (is_response(buf[0]) == FALSE)
- return TRUE;
-
- cmd = g_queue_pop_head(attrib->queue);
- if (cmd == NULL) {
- /* Keep the watch if we have events to report */
- return attrib->events != NULL;
- }
-
- if (buf[0] == ATT_OP_ERROR) {
- status = buf[4];
- goto done;
- }
-
- if (cmd->expected != buf[0]) {
- status = ATT_ECODE_IO;
- goto done;
- }
-
- status = 0;
-
-done:
- if (attrib->queue && g_queue_is_empty(attrib->queue) == FALSE)
- wake_up_sender(attrib);
-
- if (cmd) {
- if (cmd->func)
- cmd->func(status, buf, len, cmd->user_data);
-
- command_destroy(cmd);
- }
-
- return TRUE;
-}
-
-GAttrib *g_attrib_new(GIOChannel *io)
-{
- struct _GAttrib *attrib;
-
- g_io_channel_set_encoding(io, NULL, NULL);
-
- attrib = g_try_new0(struct _GAttrib, 1);
- if (attrib == NULL)
- return NULL;
-
- attrib->io = g_io_channel_ref(io);
- attrib->mtu = 512;
- attrib->queue = g_queue_new();
-
- attrib->read_watch = g_io_add_watch(attrib->io,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- received_data, attrib);
-
- return g_attrib_ref(attrib);
-}
-
-guint g_attrib_send(GAttrib *attrib, guint8 opcode, const guint8 *pdu,
- guint16 len, GAttribResultFunc func,
- gpointer user_data, GDestroyNotify notify)
-{
- struct command *c;
-
- c = g_try_new0(struct command, 1);
- if (c == NULL)
- return 0;
-
- c->opcode = opcode;
- c->expected = opcode2expected(opcode);
- c->pdu = g_malloc(len);
- memcpy(c->pdu, pdu, len);
- c->len = len;
- c->func = func;
- c->user_data = user_data;
- c->notify = notify;
- c->id = ++attrib->next_cmd_id;
-
- g_queue_push_tail(attrib->queue, c);
-
- if (g_queue_get_length(attrib->queue) == 1)
- wake_up_sender(attrib);
-
- return c->id;
-}
-
-static gint command_cmp_by_id(gconstpointer a, gconstpointer b)
-{
- const struct command *cmd = a;
- guint id = GPOINTER_TO_UINT(b);
-
- return cmd->id - id;
-}
-
-gboolean g_attrib_cancel(GAttrib *attrib, guint id)
-{
- GList *l;
- struct command *cmd;
-
- if (attrib == NULL || attrib->queue == NULL)
- return FALSE;
-
- l = g_queue_find_custom(attrib->queue, GUINT_TO_POINTER(id),
- command_cmp_by_id);
- if (l == NULL)
- return FALSE;
-
- cmd = l->data;
-
- if (cmd == g_queue_peek_head(attrib->queue) && cmd->sent)
- cmd->func = NULL;
- else {
- g_queue_remove(attrib->queue, cmd);
- command_destroy(cmd);
- }
-
- return TRUE;
-}
-
-gboolean g_attrib_cancel_all(GAttrib *attrib)
-{
- struct command *c, *head = NULL;
- gboolean first = TRUE;
-
- if (attrib == NULL || attrib->queue == NULL)
- return FALSE;
-
- while ((c = g_queue_pop_head(attrib->queue))) {
- if (first && c->sent) {
- /* If the command was sent ignore its callback ... */
- c->func = NULL;
- head = c;
- continue;
- }
-
- first = FALSE;
- command_destroy(c);
- }
-
- if (head) {
- /* ... and put it back in the queue */
- g_queue_push_head(attrib->queue, head);
- }
-
- return TRUE;
-}
-
-gboolean g_attrib_set_debug(GAttrib *attrib,
- GAttribDebugFunc func, gpointer user_data)
-{
- return TRUE;
-}
-
-guint g_attrib_register(GAttrib *attrib, guint8 opcode,
- GAttribNotifyFunc func, gpointer user_data,
- GDestroyNotify notify)
-{
- struct event *event;
-
- event = g_try_new0(struct event, 1);
- if (event == NULL)
- return 0;
-
- event->expected = opcode;
- event->func = func;
- event->user_data = user_data;
- event->notify = notify;
- event->id = ++attrib->next_evt_id;
-
- attrib->events = g_slist_append(attrib->events, event);
-
- return event->id;
-}
-
-static gint event_cmp_by_id(gconstpointer a, gconstpointer b)
-{
- const struct event *evt = a;
- guint id = GPOINTER_TO_UINT(b);
-
- return evt->id - id;
-}
-
-gboolean g_attrib_unregister(GAttrib *attrib, guint id)
-{
- struct event *evt;
- GSList *l;
-
- l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
- event_cmp_by_id);
- if (l == NULL)
- return FALSE;
-
- evt = l->data;
-
- attrib->events = g_slist_remove(attrib->events, evt);
-
- if (evt->notify)
- evt->notify(evt->user_data);
-
- g_free(evt);
-
- return TRUE;
-}
-
-gboolean g_attrib_unregister_all(GAttrib *attrib)
-{
- GSList *l;
-
- if (attrib->events == NULL)
- return FALSE;
-
- for (l = attrib->events; l; l = l->next) {
- struct event *evt = l->data;
-
- if (evt->notify)
- evt->notify(evt->user_data);
-
- g_free(evt);
- }
-
- g_slist_free(attrib->events);
- attrib->events = NULL;
-
- return TRUE;
-}
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
deleted file mode 100644
index 4306ca4..0000000
--- a/attrib/gattrib.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-#ifndef __GATTRIB_H
-#define __GATTRIB_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GATTRIB_ALL_EVENTS 0xFF
-
-struct _GAttrib;
-typedef struct _GAttrib GAttrib;
-
-typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu,
- guint16 len, gpointer user_data);
-typedef void (*GAttribDisconnectFunc)(gpointer user_data);
-typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
-typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
- gpointer user_data);
-
-GAttrib *g_attrib_new(GIOChannel *io);
-GAttrib *g_attrib_ref(GAttrib *attrib);
-void g_attrib_unref(GAttrib *attrib);
-
-gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
- GAttribDisconnectFunc disconnect, gpointer user_data);
-
-gboolean g_attrib_set_destroy_function(GAttrib *attrib,
- GDestroyNotify destroy, gpointer user_data);
-
-guint g_attrib_send(GAttrib *attrib, guint8 opcode, const guint8 *pdu,
- guint16 len, GAttribResultFunc func,
- gpointer user_data, GDestroyNotify notify);
-gboolean g_attrib_cancel(GAttrib *attrib, guint id);
-gboolean g_attrib_cancel_all(GAttrib *attrib);
-
-gboolean g_attrib_set_debug(GAttrib *attrib,
- GAttribDebugFunc func, gpointer user_data);
-
-guint g_attrib_register(GAttrib *attrib, guint8 opcode,
- GAttribNotifyFunc func, gpointer user_data,
- GDestroyNotify notify);
-
-gboolean g_attrib_unregister(GAttrib *attrib, guint id);
-gboolean g_attrib_unregister_all(GAttrib *attrib);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/att.c b/src/att.c
new file mode 100644
index 0000000..fe41d0e
--- /dev/null
+++ b/src/att.c
@@ -0,0 +1,764 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "att.h"
+
+const char *att_ecode2str(uint8_t status)
+{
+ switch (status) {
+ case ATT_ECODE_INVALID_HANDLE:
+ return "Invalid handle";
+ case ATT_ECODE_READ_NOT_PERM:
+ return "Atribute can't be read";
+ case ATT_ECODE_WRITE_NOT_PERM:
+ return "Attribute can't be written";
+ case ATT_ECODE_INVALID_PDU:
+ return "Attribute PDU was invalid";
+ case ATT_ECODE_INSUFF_AUTHEN:
+ return "Attribute requires authentication before read/write";
+ case ATT_ECODE_REQ_NOT_SUPP:
+ return "Server doesn't support the request received";
+ case ATT_ECODE_INVALID_OFFSET:
+ return "Offset past the end of the attribute";
+ case ATT_ECODE_INSUFF_AUTHO:
+ return "Attribute requires authorization before read/write";
+ case ATT_ECODE_PREP_QUEUE_FULL:
+ return "Too many prepare writes have been queued";
+ case ATT_ECODE_ATTR_NOT_FOUND:
+ return "No attribute found within the given range";
+ case ATT_ECODE_ATTR_NOT_LONG:
+ return "Attribute can't be read/written using Read Blob Req";
+ case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
+ return "Encryption Key Size is insufficient";
+ case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
+ return "Attribute value length is invalid";
+ case ATT_ECODE_UNLIKELY:
+ return "Request attribute has encountered an unlikely error";
+ case ATT_ECODE_INSUFF_ENC:
+ return "Encryption required before read/write";
+ case ATT_ECODE_UNSUPP_GRP_TYPE:
+ return "Attribute type is not a supported grouping attribute";
+ case ATT_ECODE_INSUFF_RESOURCES:
+ return "Insufficient Resources to complete the request";
+ case ATT_ECODE_IO:
+ return "Internal application error: I/O";
+ default:
+ return "Unexpected error code";
+ }
+}
+
+void att_data_list_free(struct att_data_list *list)
+{
+ int i;
+
+ for (i = 0; i < list->num; i++)
+ free(list->data[i]);
+
+ free(list->data);
+ free(list);
+}
+
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+ uint16_t length;
+
+ if (!uuid)
+ return 0;
+
+ if (uuid->type == SDP_UUID16)
+ length = 2;
+ else if (uuid->type == SDP_UUID128)
+ length = 16;
+ else
+ return 0;
+
+ if (len < min_len + length)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ if (uuid->type == SDP_UUID16)
+ att_put_u16(uuid->value.uuid16, &pdu[5]);
+ else
+ memcpy(&pdu[5], &uuid->value.uuid128, length);
+
+ return min_len + length;
+}
+
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, uuid_t *uuid)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (start == NULL || end == NULL || uuid == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
+ return 0;
+
+ if (len < min_len + 2)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+ if (len == min_len + 2)
+ sdp_uuid16_create(uuid, att_get_u16(&pdu[5]));
+ else
+ sdp_uuid128_create(uuid, &pdu[5]);
+
+ return len;
+}
+
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
+ int len)
+{
+ int i;
+ uint16_t w;
+ uint8_t *ptr;
+
+ if (list == NULL)
+ return 0;
+
+ if (len < list->len + 2)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_GROUP_RESP;
+ pdu[1] = list->len;
+
+ ptr = &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+ memcpy(ptr, list->data[i], list->len);
+ ptr += list->len;
+ w += list->len;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len)
+{
+ struct att_data_list *list;
+ const uint8_t *ptr;
+ int i;
+
+ if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
+ return NULL;
+
+ list = malloc(sizeof(struct att_data_list));
+ list->len = pdu[1];
+ list->num = (len - 2) / list->len;
+
+ list->data = malloc(sizeof(uint8_t *) * list->num);
+ ptr = &pdu[2];
+
+ for (i = 0; i < list->num; i++) {
+ list->data[i] = malloc(sizeof(uint8_t) * list->len);
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ return 0;
+}
+
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+ uint16_t length;
+
+ if (!uuid)
+ return 0;
+
+ if (uuid->type == SDP_UUID16)
+ length = 2;
+ else if (uuid->type == SDP_UUID128)
+ length = 16;
+ else
+ return 0;
+
+ if (len < min_len + length)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ if (uuid->type == SDP_UUID16)
+ att_put_u16(uuid->value.uuid16, &pdu[5]);
+ else
+ memcpy(&pdu[5], &uuid->value.uuid128, length);
+
+ return min_len + length;
+}
+
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, uuid_t *uuid)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (start == NULL || end == NULL || uuid == NULL)
+ return 0;
+
+ if (len < min_len + 2)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+
+ if (len == min_len + 2)
+ sdp_uuid16_create(uuid, att_get_u16(&pdu[5]));
+ else
+ sdp_uuid128_create(uuid, &pdu[5]);
+
+ return len;
+}
+
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, int len)
+{
+ uint8_t *ptr;
+ int i, w;
+
+ if (list == NULL)
+ return 0;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < list->len + 2)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_BY_TYPE_RESP;
+ pdu[1] = list->len;
+ ptr = &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+ memcpy(ptr, list->data[i], list->len);
+ ptr += list->len;
+ w += list->len;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len)
+{
+ struct att_data_list *list;
+ const uint8_t *ptr;
+ int i;
+
+ if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
+ return NULL;
+
+ list = malloc(sizeof(struct att_data_list));
+ list->len = pdu[1];
+ list->num = (len - 2) / list->len;
+
+ list->data = malloc(sizeof(uint8_t *) * list->num);
+ ptr = &pdu[2];
+
+ for (i = 0; i < list->num; i++) {
+ list->data[i] = malloc(sizeof(uint8_t) * list->len);
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_WRITE_CMD;
+ att_put_u16(handle, &pdu[1]);
+
+ if (vlen > 0) {
+ memcpy(&pdu[3], value, vlen);
+ return min_len + vlen;
+ }
+
+ return min_len;
+}
+
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL || handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_WRITE_CMD)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ memcpy(value, pdu + min_len, len - min_len);
+ *vlen = len - min_len;
+
+ return len;
+}
+
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (vlen > len - min_len)
+ vlen = len - min_len;
+
+ pdu[0] = ATT_OP_WRITE_REQ;
+ att_put_u16(handle, &pdu[1]);
+
+ if (vlen > 0) {
+ memcpy(&pdu[3], value, vlen);
+ return min_len + vlen;
+ }
+
+ return min_len;
+}
+
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL || handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_WRITE_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ *vlen = len - min_len;
+ if (*vlen > 0)
+ memcpy(value, pdu + min_len, *vlen);
+
+ return len;
+}
+
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_READ_REQ;
+ att_put_u16(handle, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (handle == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
+
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ /* If the attribute value length is longer than the allowed PDU size,
+ * send only the octets that fit on the PDU. The remaining octets can
+ * be requested using the Read Blob Request. */
+ if (vlen > len - 1)
+ vlen = len - 1;
+
+ pdu[0] = ATT_OP_READ_RESP;
+
+ memcpy(pdu + 1, value, vlen);
+
+ return vlen + 1;
+}
+
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen)
+{
+ if (pdu == NULL)
+ return 0;
+
+ if (value == NULL || vlen == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_RESP)
+ return 0;
+
+ memcpy(value, pdu + 1, len - 1);
+
+ *vlen = len - 1;
+
+ return len;
+}
+
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+ uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
+ sizeof(handle) + sizeof(status);
+ uint16_t u16;
+
+ if (len < min_len)
+ return 0;
+
+ u16 = htobs(handle);
+ pdu[0] = ATT_OP_ERROR;
+ pdu[1] = opcode;
+ memcpy(&pdu[2], &u16, sizeof(u16));
+ pdu[4] = status;
+
+ return min_len;
+}
+
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_FIND_INFO_REQ;
+ att_put_u16(start, &pdu[1]);
+ att_put_u16(end, &pdu[3]);
+
+ return min_len;
+}
+
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (start == NULL || end == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_FIND_INFO_REQ)
+ return 0;
+
+ *start = att_get_u16(&pdu[1]);
+ *end = att_get_u16(&pdu[3]);
+
+ return min_len;
+}
+
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+ uint8_t *pdu, int len)
+{
+ uint8_t *ptr;
+ int i, w;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (list == NULL)
+ return 0;
+
+ if (len < list->len + 2)
+ return 0;
+
+ pdu[0] = ATT_OP_FIND_INFO_RESP;
+ pdu[1] = format;
+ ptr = (void *) &pdu[2];
+
+ for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+ memcpy(ptr, list->data[i], list->len);
+ ptr += list->len;
+ w += list->len;
+ }
+
+ return w;
+}
+
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+ uint8_t *format)
+{
+ struct att_data_list *list;
+ uint8_t *ptr;
+ int i;
+
+ if (pdu == NULL)
+ return 0;
+
+ if (format == NULL)
+ return 0;
+
+ if (pdu[0] != ATT_OP_FIND_INFO_RESP)
+ return 0;
+
+ *format = pdu[1];
+
+ list = malloc(sizeof(struct att_data_list));
+
+ list->len = sizeof(pdu[0]) + sizeof(*format);
+ if (*format == 0x01)
+ list->len += 2;
+ else if (*format == 0x02)
+ list->len += 16;
+
+ list->num = (len - 2) / list->len;
+ list->data = malloc(sizeof(uint8_t *) * list->num);
+
+ ptr = (void *) &pdu[2];
+
+ for (i = 0; i < list->num; i++) {
+ list->data[i] = malloc(list->len);
+ memcpy(list->data[i], ptr, list->len);
+ ptr += list->len;
+ }
+
+ return list;
+}
+
+uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < (a->len + min_len))
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_NOTIFY;
+ att_put_u16(a->handle, &pdu[1]);
+ memcpy(&pdu[3], a->data, a->len);
+
+ return a->len + min_len;
+}
+
+uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < (a->len + min_len))
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_IND;
+ att_put_u16(a->handle, &pdu[1]);
+ memcpy(&pdu[3], a->data, a->len);
+
+ return a->len + min_len;
+}
+
+struct attribute *dec_indication(const uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ struct attribute *a;
+
+ if (pdu == NULL)
+ return NULL;
+
+ if (pdu[0] != ATT_OP_HANDLE_IND)
+ return NULL;
+
+ if (len < min_len)
+ return NULL;
+
+ a = malloc(sizeof(struct attribute) + len - min_len);
+ if (a == NULL)
+ return NULL;
+
+ a->len = len - min_len;
+
+ a->handle = att_get_u16(&pdu[1]);
+ memcpy(a->data, &pdu[3], a->len);
+
+ return a;
+}
+
+uint16_t enc_confirmation(uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_CNF;
+
+ return min_len;
+}
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_MTU_REQ;
+ att_put_u16(mtu, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (mtu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_MTU_REQ)
+ return 0;
+
+ *mtu = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
+
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ pdu[0] = ATT_OP_MTU_RESP;
+ att_put_u16(mtu, &pdu[1]);
+
+ return min_len;
+}
+
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (mtu == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_MTU_RESP)
+ return 0;
+
+ *mtu = att_get_u16(&pdu[1]);
+
+ return min_len;
+}
diff --git a/src/att.h b/src/att.h
new file mode 100644
index 0000000..ea49dc2
--- /dev/null
+++ b/src/att.h
@@ -0,0 +1,206 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+/* GATT Profile Attribute types */
+#define GATT_PRIM_SVC_UUID 0x2800
+#define GATT_SND_SVC_UUID 0x2801
+#define GATT_INCLUDE_UUID 0x2802
+#define GATT_CHARAC_UUID 0x2803
+
+/* GATT Characteristic Types */
+#define GATT_CHARAC_DEVICE_NAME 0x2A00
+#define GATT_CHARAC_APPEARANCE 0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
+
+/* GATT Characteristic Descriptors */
+#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
+#define GATT_CHARAC_USER_DESC_UUID 0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
+#define GATT_SERVER_CHARAC_CFG_UUID 0x2903
+#define GATT_CHARAC_FMT_UUID 0x2904
+#define GATT_CHARAC_AGREG_FMT_UUID 0x2905
+
+/* Attribute Protocol Opcodes */
+#define ATT_OP_ERROR 0x01
+#define ATT_OP_MTU_REQ 0x02
+#define ATT_OP_MTU_RESP 0x03
+#define ATT_OP_FIND_INFO_REQ 0x04
+#define ATT_OP_FIND_INFO_RESP 0x05
+#define ATT_OP_FIND_BY_TYPE_REQ 0x06
+#define ATT_OP_FIND_BY_TYPE_RESP 0x07
+#define ATT_OP_READ_BY_TYPE_REQ 0x08
+#define ATT_OP_READ_BY_TYPE_RESP 0x09
+#define ATT_OP_READ_REQ 0x0A
+#define ATT_OP_READ_RESP 0x0B
+#define ATT_OP_READ_BLOB_REQ 0x0C
+#define ATT_OP_READ_BLOB_RESP 0x0D
+#define ATT_OP_READ_MULTI_REQ 0x0E
+#define ATT_OP_READ_MULTI_RESP 0x0F
+#define ATT_OP_READ_BY_GROUP_REQ 0x10
+#define ATT_OP_READ_BY_GROUP_RESP 0x11
+#define ATT_OP_WRITE_REQ 0x12
+#define ATT_OP_WRITE_RESP 0x13
+#define ATT_OP_WRITE_CMD 0x52
+#define ATT_OP_PREP_WRITE_REQ 0x16
+#define ATT_OP_PREP_WRITE_RESP 0x17
+#define ATT_OP_EXEC_WRITE_REQ 0x18
+#define ATT_OP_EXEC_WRITE_RESP 0x19
+#define ATT_OP_HANDLE_NOTIFY 0x1B
+#define ATT_OP_HANDLE_IND 0x1D
+#define ATT_OP_HANDLE_CNF 0x1E
+#define ATT_OP_SIGNED_WRITE_CMD 0xD2
+
+/* Error codes for Error response PDU */
+#define ATT_ECODE_INVALID_HANDLE 0x01
+#define ATT_ECODE_READ_NOT_PERM 0x02
+#define ATT_ECODE_WRITE_NOT_PERM 0x03
+#define ATT_ECODE_INVALID_PDU 0x04
+#define ATT_ECODE_INSUFF_AUTHEN 0x05
+#define ATT_ECODE_REQ_NOT_SUPP 0x06
+#define ATT_ECODE_INVALID_OFFSET 0x07
+#define ATT_ECODE_INSUFF_AUTHO 0x08
+#define ATT_ECODE_PREP_QUEUE_FULL 0x09
+#define ATT_ECODE_ATTR_NOT_FOUND 0x0A
+#define ATT_ECODE_ATTR_NOT_LONG 0x0B
+#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE 0x0C
+#define ATT_ECODE_INVAL_ATTR_VALUE_LEN 0x0D
+#define ATT_ECODE_UNLIKELY 0x0E
+#define ATT_ECODE_INSUFF_ENC 0x0F
+#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10
+#define ATT_ECODE_INSUFF_RESOURCES 0x11
+/* Application error */
+#define ATT_ECODE_IO 0xFF
+
+/* Characteristic Property bit field */
+#define ATT_CHAR_PROPER_BROADCAST 0x01
+#define ATT_CHAR_PROPER_READ 0x02
+#define ATT_CHAR_PROPER_WRITE_WITHOUT_RESP 0x04
+#define ATT_CHAR_PROPER_WRITE 0x08
+#define ATT_CHAR_PROPER_NOTIFY 0x10
+#define ATT_CHAR_PROPER_INDICATE 0x20
+#define ATT_CHAR_PROPER_AUTH 0x40
+#define ATT_CHAR_PROPER_EXT_PROPER 0x80
+
+
+#define ATT_MAX_MTU 256
+#define ATT_DEFAULT_MTU 23
+
+struct attribute {
+ uint16_t handle;
+ uuid_t uuid;
+ int len;
+ uint8_t data[0];
+};
+
+struct att_data_list {
+ uint16_t num;
+ uint16_t len;
+ uint8_t **data;
+};
+
+/* These functions do byte conversion */
+static inline uint8_t att_get_u8(const void *ptr)
+{
+ const uint8_t *u8_ptr = ptr;
+ return bt_get_unaligned(u8_ptr);
+}
+
+static inline uint16_t att_get_u16(const void *ptr)
+{
+ const uint16_t *u16_ptr = ptr;
+ return btohs(bt_get_unaligned(u16_ptr));
+}
+
+static inline uint32_t att_get_u32(const void *ptr)
+{
+ const uint32_t *u32_ptr = ptr;
+ return btohl(bt_get_unaligned(u32_ptr));
+}
+
+static inline void att_put_u8(uint8_t src, void *dst)
+{
+ bt_put_unaligned(src, (uint8_t *) dst);
+}
+
+static inline void att_put_u16(uint16_t src, void *dst)
+{
+ bt_put_unaligned(htobs(src), (uint16_t *) dst);
+}
+
+static inline void att_put_u32(uint16_t src, void *dst)
+{
+ bt_put_unaligned(htobl(src), (uint32_t *) dst);
+}
+
+void att_data_list_free(struct att_data_list *list);
+
+const char *att_ecode2str(uint8_t status);
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len);
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, uuid_t *uuid);
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, int len);
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len);
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len);
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, uuid_t *uuid,
+ uint8_t *pdu, int len);
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end, uuid_t *uuid);
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
+ int len);
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen);
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len);
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int *vlen);
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len);
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle);
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen);
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+ uint8_t *pdu, int len);
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len);
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+ uint16_t *end);
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+ uint8_t *pdu, int len);
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+ uint8_t *format);
+uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len);
+uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len);
+struct attribute *dec_indication(const uint8_t *pdu, int len);
+uint16_t enc_confirmation(uint8_t *pdu, int len);
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu);
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu);
diff --git a/src/gatt.c b/src/gatt.c
new file mode 100644
index 0000000..24ec990
--- /dev/null
+++ b/src/gatt.c
@@ -0,0 +1,113 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <glib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+
+guint gatt_discover_primary(GAttrib *attrib, uint16_t start,
+ uint16_t end, GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ uuid_t uuid;
+ guint16 plen;
+
+ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+
+ plen = enc_read_by_grp_req(start, end, &uuid, pdu, sizeof(pdu));
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, ATT_OP_READ_BY_GROUP_REQ,
+ pdu, plen, func, user_data, NULL);
+}
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ uuid_t uuid;
+ guint16 plen;
+
+ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+ plen = enc_read_by_type_req(start, end, &uuid, pdu, sizeof(pdu));
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, ATT_OP_READ_BY_TYPE_REQ,
+ pdu, plen, func, user_data, NULL);
+}
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
+ gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint16 plen;
+
+ plen = enc_read_req(handle, pdu, sizeof(pdu));
+ return g_attrib_send(attrib, ATT_OP_READ_REQ, pdu, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+ int vlen, GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint16 plen;
+
+ plen = enc_write_req(handle, value, vlen, pdu, sizeof(pdu));
+ return g_attrib_send(attrib, ATT_OP_WRITE_REQ, pdu, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint16 plen;
+
+ plen = enc_find_info_req(start, end, pdu, sizeof(pdu));
+ if (plen == 0)
+ return 0;
+
+ return g_attrib_send(attrib, ATT_OP_FIND_INFO_REQ, pdu, plen, func,
+ user_data, NULL);
+}
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+ GDestroyNotify notify, gpointer user_data)
+{
+ uint8_t pdu[ATT_DEFAULT_MTU];
+ guint16 plen;
+
+ plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
+ return g_attrib_send(attrib, ATT_OP_WRITE_CMD, pdu, plen, NULL,
+ user_data, notify);
+}
diff --git a/src/gatt.h b/src/gatt.h
new file mode 100644
index 0000000..f1599c2
--- /dev/null
+++ b/src/gatt.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define GATT_CID 4
+
+guint gatt_discover_primary(GAttrib *attrib, uint16_t start,
+ uint16_t end, GAttribResultFunc func, gpointer user_data);
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data);
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
+ gpointer user_data);
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+ int vlen, GAttribResultFunc func, gpointer user_data);
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+ GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+ GDestroyNotify notify, gpointer user_data);
diff --git a/src/gattrib.c b/src/gattrib.c
new file mode 100644
index 0000000..ed18168
--- /dev/null
+++ b/src/gattrib.c
@@ -0,0 +1,535 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+
+#include <stdio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include "att.h"
+#include "gattrib.h"
+
+struct _GAttrib {
+ GIOChannel *io;
+ gint refs;
+ gint mtu;
+ guint read_watch;
+ guint write_watch;
+ GQueue *queue;
+ GSList *events;
+ guint next_cmd_id;
+ guint next_evt_id;
+ GDestroyNotify destroy;
+ GAttribDisconnectFunc disconnect;
+ gpointer destroy_user_data;
+ gpointer disc_user_data;
+};
+
+struct command {
+ guint id;
+ guint8 opcode;
+ guint8 *pdu;
+ guint16 len;
+ guint8 expected;
+ gboolean sent;
+ GAttribResultFunc func;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+struct event {
+ guint id;
+ guint8 expected;
+ GAttribNotifyFunc func;
+ gpointer user_data;
+ GDestroyNotify notify;
+};
+
+static guint8 opcode2expected(guint8 opcode)
+{
+ switch (opcode) {
+ case ATT_OP_MTU_REQ:
+ return ATT_OP_MTU_RESP;
+
+ case ATT_OP_FIND_INFO_REQ:
+ return ATT_OP_FIND_INFO_RESP;
+
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ return ATT_OP_FIND_BY_TYPE_RESP;
+
+ case ATT_OP_READ_BY_TYPE_REQ:
+ return ATT_OP_READ_BY_TYPE_RESP;
+
+ case ATT_OP_READ_REQ:
+ return ATT_OP_READ_RESP;
+
+ case ATT_OP_READ_BLOB_REQ:
+ return ATT_OP_READ_BLOB_RESP;
+
+ case ATT_OP_READ_MULTI_REQ:
+ return ATT_OP_READ_MULTI_RESP;
+
+ case ATT_OP_READ_BY_GROUP_REQ:
+ return ATT_OP_READ_BY_GROUP_RESP;
+
+ case ATT_OP_WRITE_REQ:
+ return ATT_OP_WRITE_RESP;
+
+ case ATT_OP_PREP_WRITE_REQ:
+ return ATT_OP_PREP_WRITE_RESP;
+
+ case ATT_OP_EXEC_WRITE_REQ:
+ return ATT_OP_EXEC_WRITE_RESP;
+
+ case ATT_OP_HANDLE_IND:
+ return ATT_OP_HANDLE_CNF;
+ }
+
+ return 0;
+}
+
+static gboolean is_response(guint8 opcode)
+{
+ switch (opcode) {
+ case ATT_OP_ERROR:
+ case ATT_OP_MTU_RESP:
+ case ATT_OP_FIND_INFO_RESP:
+ case ATT_OP_FIND_BY_TYPE_RESP:
+ case ATT_OP_READ_BY_TYPE_RESP:
+ case ATT_OP_READ_RESP:
+ case ATT_OP_READ_BLOB_RESP:
+ case ATT_OP_READ_MULTI_RESP:
+ case ATT_OP_READ_BY_GROUP_RESP:
+ case ATT_OP_WRITE_RESP:
+ case ATT_OP_PREP_WRITE_RESP:
+ case ATT_OP_EXEC_WRITE_RESP:
+ case ATT_OP_HANDLE_CNF:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+GAttrib *g_attrib_ref(GAttrib *attrib)
+{
+ if (!attrib)
+ return NULL;
+
+ g_atomic_int_inc(&attrib->refs);
+
+ return attrib;
+}
+
+static void command_destroy(struct command *cmd)
+{
+ if (cmd->notify)
+ cmd->notify(cmd->user_data);
+
+ g_free(cmd->pdu);
+ g_free(cmd);
+}
+
+static void event_destroy(struct event *evt)
+{
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+}
+
+void g_attrib_unref(GAttrib *attrib)
+{
+ GSList *l;
+ struct command *c;
+
+ if (!attrib)
+ return;
+
+ if (g_atomic_int_dec_and_test(&attrib->refs) == FALSE)
+ return;
+
+ while ((c = g_queue_pop_head(attrib->queue)))
+ command_destroy(c);
+
+ attrib->queue = NULL;
+
+ for (l = attrib->events; l; l = l->next)
+ event_destroy(l->data);
+
+ g_slist_free(attrib->events);
+ attrib->events = NULL;
+
+ if (attrib->write_watch > 0)
+ g_source_remove(attrib->write_watch);
+
+ if (attrib->read_watch > 0) {
+ g_source_remove(attrib->read_watch);
+ g_io_channel_unref(attrib->io);
+ }
+
+
+ if (attrib->destroy)
+ attrib->destroy(attrib->destroy_user_data);
+
+ g_free(attrib);
+}
+
+gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
+ GAttribDisconnectFunc disconnect, gpointer user_data)
+{
+ if (attrib == NULL)
+ return FALSE;
+
+ attrib->disconnect = disconnect;
+ attrib->disc_user_data = user_data;
+
+ return TRUE;
+}
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+ GDestroyNotify destroy, gpointer user_data)
+{
+ if (attrib == NULL)
+ return FALSE;
+
+ attrib->destroy = destroy;
+ attrib->destroy_user_data = user_data;
+
+ return TRUE;
+}
+
+static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
+ gpointer data)
+{
+ struct _GAttrib *attrib = data;
+ struct command *cmd;
+ GError *gerr = NULL;
+ gsize len;
+ GIOStatus iostat;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ if (attrib->disconnect)
+ attrib->disconnect(attrib->disc_user_data);
+
+ return FALSE;
+ }
+
+ cmd = g_queue_peek_head(attrib->queue);
+ if (cmd == NULL)
+ return FALSE;
+
+ iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
+ &len, &gerr);
+ if (iostat != G_IO_STATUS_NORMAL)
+ return FALSE;
+
+ g_io_channel_flush(io, NULL);
+
+ if (cmd->expected == 0) {
+ g_queue_pop_head(attrib->queue);
+ command_destroy(cmd);
+
+ return TRUE;
+ }
+
+ cmd->sent = TRUE;
+
+ return FALSE;
+}
+
+static void destroy_sender(gpointer data)
+{
+ struct _GAttrib *attrib = data;
+
+ attrib->write_watch = 0;
+}
+
+static void wake_up_sender(struct _GAttrib *attrib)
+{
+ if (attrib->write_watch == 0)
+ attrib->write_watch = g_io_add_watch_full(attrib->io,
+ G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data,
+ attrib, destroy_sender);
+}
+
+static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ struct _GAttrib *attrib = data;
+ struct command *cmd = NULL;
+ GSList *l;
+ uint8_t buf[512], status;
+ gsize len;
+ GIOStatus iostat;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ attrib->read_watch = 0;
+ if (attrib->disconnect)
+ attrib->disconnect(attrib->disc_user_data);
+ return FALSE;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf),
+ &len, NULL);
+ if (iostat != G_IO_STATUS_NORMAL) {
+ status = ATT_ECODE_IO;
+ goto done;
+ }
+
+ for (l = attrib->events; l; l = l->next) {
+ struct event *evt = l->data;
+
+ if (evt->expected == buf[0] ||
+ evt->expected == GATTRIB_ALL_EVENTS)
+ evt->func(buf, len, evt->user_data);
+ }
+
+ if (is_response(buf[0]) == FALSE)
+ return TRUE;
+
+ cmd = g_queue_pop_head(attrib->queue);
+ if (cmd == NULL) {
+ /* Keep the watch if we have events to report */
+ return attrib->events != NULL;
+ }
+
+ if (buf[0] == ATT_OP_ERROR) {
+ status = buf[4];
+ goto done;
+ }
+
+ if (cmd->expected != buf[0]) {
+ status = ATT_ECODE_IO;
+ goto done;
+ }
+
+ status = 0;
+
+done:
+ if (attrib->queue && g_queue_is_empty(attrib->queue) == FALSE)
+ wake_up_sender(attrib);
+
+ if (cmd) {
+ if (cmd->func)
+ cmd->func(status, buf, len, cmd->user_data);
+
+ command_destroy(cmd);
+ }
+
+ return TRUE;
+}
+
+GAttrib *g_attrib_new(GIOChannel *io)
+{
+ struct _GAttrib *attrib;
+
+ g_io_channel_set_encoding(io, NULL, NULL);
+
+ attrib = g_try_new0(struct _GAttrib, 1);
+ if (attrib == NULL)
+ return NULL;
+
+ attrib->io = g_io_channel_ref(io);
+ attrib->mtu = 512;
+ attrib->queue = g_queue_new();
+
+ attrib->read_watch = g_io_add_watch(attrib->io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ received_data, attrib);
+
+ return g_attrib_ref(attrib);
+}
+
+guint g_attrib_send(GAttrib *attrib, guint8 opcode, const guint8 *pdu,
+ guint16 len, GAttribResultFunc func,
+ gpointer user_data, GDestroyNotify notify)
+{
+ struct command *c;
+
+ c = g_try_new0(struct command, 1);
+ if (c == NULL)
+ return 0;
+
+ c->opcode = opcode;
+ c->expected = opcode2expected(opcode);
+ c->pdu = g_malloc(len);
+ memcpy(c->pdu, pdu, len);
+ c->len = len;
+ c->func = func;
+ c->user_data = user_data;
+ c->notify = notify;
+ c->id = ++attrib->next_cmd_id;
+
+ g_queue_push_tail(attrib->queue, c);
+
+ if (g_queue_get_length(attrib->queue) == 1)
+ wake_up_sender(attrib);
+
+ return c->id;
+}
+
+static gint command_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct command *cmd = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return cmd->id - id;
+}
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id)
+{
+ GList *l;
+ struct command *cmd;
+
+ if (attrib == NULL || attrib->queue == NULL)
+ return FALSE;
+
+ l = g_queue_find_custom(attrib->queue, GUINT_TO_POINTER(id),
+ command_cmp_by_id);
+ if (l == NULL)
+ return FALSE;
+
+ cmd = l->data;
+
+ if (cmd == g_queue_peek_head(attrib->queue) && cmd->sent)
+ cmd->func = NULL;
+ else {
+ g_queue_remove(attrib->queue, cmd);
+ command_destroy(cmd);
+ }
+
+ return TRUE;
+}
+
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+ struct command *c, *head = NULL;
+ gboolean first = TRUE;
+
+ if (attrib == NULL || attrib->queue == NULL)
+ return FALSE;
+
+ while ((c = g_queue_pop_head(attrib->queue))) {
+ if (first && c->sent) {
+ /* If the command was sent ignore its callback ... */
+ c->func = NULL;
+ head = c;
+ continue;
+ }
+
+ first = FALSE;
+ command_destroy(c);
+ }
+
+ if (head) {
+ /* ... and put it back in the queue */
+ g_queue_push_head(attrib->queue, head);
+ }
+
+ return TRUE;
+}
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+ GAttribDebugFunc func, gpointer user_data)
+{
+ return TRUE;
+}
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+ GAttribNotifyFunc func, gpointer user_data,
+ GDestroyNotify notify)
+{
+ struct event *event;
+
+ event = g_try_new0(struct event, 1);
+ if (event == NULL)
+ return 0;
+
+ event->expected = opcode;
+ event->func = func;
+ event->user_data = user_data;
+ event->notify = notify;
+ event->id = ++attrib->next_evt_id;
+
+ attrib->events = g_slist_append(attrib->events, event);
+
+ return event->id;
+}
+
+static gint event_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+ const struct event *evt = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return evt->id - id;
+}
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id)
+{
+ struct event *evt;
+ GSList *l;
+
+ l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
+ event_cmp_by_id);
+ if (l == NULL)
+ return FALSE;
+
+ evt = l->data;
+
+ attrib->events = g_slist_remove(attrib->events, evt);
+
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+
+ return TRUE;
+}
+
+gboolean g_attrib_unregister_all(GAttrib *attrib)
+{
+ GSList *l;
+
+ if (attrib->events == NULL)
+ return FALSE;
+
+ for (l = attrib->events; l; l = l->next) {
+ struct event *evt = l->data;
+
+ if (evt->notify)
+ evt->notify(evt->user_data);
+
+ g_free(evt);
+ }
+
+ g_slist_free(attrib->events);
+ attrib->events = NULL;
+
+ return TRUE;
+}
diff --git a/src/gattrib.h b/src/gattrib.h
new file mode 100644
index 0000000..4306ca4
--- /dev/null
+++ b/src/gattrib.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef __GATTRIB_H
+#define __GATTRIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GATTRIB_ALL_EVENTS 0xFF
+
+struct _GAttrib;
+typedef struct _GAttrib GAttrib;
+
+typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data);
+typedef void (*GAttribDisconnectFunc)(gpointer user_data);
+typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
+typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
+ gpointer user_data);
+
+GAttrib *g_attrib_new(GIOChannel *io);
+GAttrib *g_attrib_ref(GAttrib *attrib);
+void g_attrib_unref(GAttrib *attrib);
+
+gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
+ GAttribDisconnectFunc disconnect, gpointer user_data);
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+ GDestroyNotify destroy, gpointer user_data);
+
+guint g_attrib_send(GAttrib *attrib, guint8 opcode, const guint8 *pdu,
+ guint16 len, GAttribResultFunc func,
+ gpointer user_data, GDestroyNotify notify);
+gboolean g_attrib_cancel(GAttrib *attrib, guint id);
+gboolean g_attrib_cancel_all(GAttrib *attrib);
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+ GAttribDebugFunc func, gpointer user_data);
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+ GAttribNotifyFunc func, gpointer user_data,
+ GDestroyNotify notify);
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id);
+gboolean g_attrib_unregister_all(GAttrib *attrib);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--
1.7.3.2
^ permalink raw reply related
* [PATCH] Fix invalid reference to GATT service structure
From: Claudio Takahasi @ 2010-11-04 19:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
In-Reply-To: <1288899810-18722-1-git-send-email-claudio.takahasi@openbossa.org>
---
attrib/client.c | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/attrib/client.c b/attrib/client.c
index bcc903b..1f2c217 100644
--- a/attrib/client.c
+++ b/attrib/client.c
@@ -1342,13 +1342,9 @@ int attrib_client_register(struct btd_device *device, int psm)
bacpy(&gatt->dba, &dba);
gatt->psm = psm;
- gatt_services = g_slist_append(gatt_services, gatt);
-
- /* FIXME: we should also listen for incoming connections */
-
if (load_primary_services(gatt)) {
DBG("Primary services loaded");
- return 0;
+ goto done;
}
if (psm < 0) {
@@ -1385,6 +1381,9 @@ int attrib_client_register(struct btd_device *device, int psm)
g_attrib_set_disconnect_function(gatt->attrib, attrib_disconnect,
gatt);
+done:
+ gatt_services = g_slist_append(gatt_services, gatt);
+
return 0;
}
--
1.7.3.2
^ permalink raw reply related
* [PATCH] Enable GATT over LE link on the attribute client
From: Claudio Takahasi @ 2010-11-04 19:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
In-Reply-To: <1288899810-18722-1-git-send-email-claudio.takahasi@openbossa.org>
---
attrib/client.c | 22 ++++++++++++----------
attrib/gatt.h | 2 ++
attrib/gatttool.c | 1 -
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/attrib/client.c b/attrib/client.c
index cd720e6..bcc903b 100644
--- a/attrib/client.c
+++ b/attrib/client.c
@@ -1352,23 +1352,25 @@ int attrib_client_register(struct btd_device *device, int psm)
}
if (psm < 0) {
- /*
- * FIXME: when PSM is not given means that L2CAP fixed
- * channel shall be used. For this case, ATT CID(0x0004).
- */
-
- DBG("GATT over LE");
-
- return 0;
- }
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &dba,
+ BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
- io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, &gerr,
+ DBG("GATT over Low Energy");
+ } else {
+ io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_PSM, psm,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
+ DBG("GATT over Basic Rate");
+ }
+
if (!io) {
error("%s", gerr->message);
g_error_free(gerr);
diff --git a/attrib/gatt.h b/attrib/gatt.h
index a357f58..f1599c2 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -22,6 +22,8 @@
*
*/
+#define GATT_CID 4
+
guint gatt_discover_primary(GAttrib *attrib, uint16_t start,
uint16_t end, GAttribResultFunc func, gpointer user_data);
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index 2de3f8b..b9f5138 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -45,7 +45,6 @@
/* Minimum MTU for L2CAP connections over BR/EDR */
#define ATT_MIN_MTU_L2CAP 48
-#define GATT_CID 4
static gchar *opt_src = NULL;
static gchar *opt_dst = NULL;
--
1.7.3.2
^ permalink raw reply related
* [PATCH] Move set_nonblocking function to audio/unix.c
From: Claudio Takahasi @ 2010-11-04 19:43 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Claudio Takahasi
---
audio/unix.c | 20 ++++++++++++++++++++
src/glib-helper.c | 20 --------------------
src/glib-helper.h | 2 --
3 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/audio/unix.c b/audio/unix.c
index ad822bd..62eee31 100644
--- a/audio/unix.c
+++ b/audio/unix.c
@@ -27,6 +27,7 @@
#endif
#include <stdio.h>
+#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
@@ -115,6 +116,25 @@ static void client_free(struct unix_client *client)
g_free(client);
}
+static int set_nonblocking(int fd)
+{
+ long arg;
+
+ arg = fcntl(fd, F_GETFL);
+ if (arg < 0)
+ return -errno;
+
+ /* Return if already nonblocking */
+ if (arg & O_NONBLOCK)
+ return 0;
+
+ arg |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, arg) < 0)
+ return -errno;
+
+ return 0;
+}
+
/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly
* AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct
* cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the
diff --git a/src/glib-helper.c b/src/glib-helper.c
index b3c5003..9d76626 100644
--- a/src/glib-helper.c
+++ b/src/glib-helper.c
@@ -27,7 +27,6 @@
#include <stdlib.h>
#include <errno.h>
-#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -113,25 +112,6 @@ static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
cached);
}
-int set_nonblocking(int fd)
-{
- long arg;
-
- arg = fcntl(fd, F_GETFL);
- if (arg < 0)
- return -errno;
-
- /* Return if already nonblocking */
- if (arg & O_NONBLOCK)
- return 0;
-
- arg |= O_NONBLOCK;
- if (fcntl(fd, F_SETFL, arg) < 0)
- return -errno;
-
- return 0;
-}
-
struct search_context {
bdaddr_t src;
bdaddr_t dst;
diff --git a/src/glib-helper.h b/src/glib-helper.h
index 8a334e9..e89c2c6 100644
--- a/src/glib-helper.h
+++ b/src/glib-helper.h
@@ -21,8 +21,6 @@
*
*/
-int set_nonblocking(int fd);
-
typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
typedef void (*bt_destroy_t) (gpointer user_data);
--
1.7.3.2
^ permalink raw reply related
* Re: [PATCH] Fix handling call waiting indicators in telephony
From: Johan Hedberg @ 2010-11-04 18:18 UTC (permalink / raw)
To: Lukasz Pawlik; +Cc: linux-bluetooth
In-Reply-To: <1288882837-9163-1-git-send-email-lucas.pawlik@gmail.com>
Hi Lukasz,
On Thu, Nov 04, 2010, Lukasz Pawlik wrote:
> Previously +CIEV: "callsetup" was sent before +CCWA: (call waiting)
> notification. According to the HFP specification AG reports new
> call waiting by sending +CCWA before +CIEV indicator. Some HF did
> not handle properly ending active call after rejecting 2nd incoming call
> if "callsetup" indicator was sent before call waiting.
> ---
> audio/telephony-maemo6.c | 6 +++---
> 1 files changed, 3 insertions(+), 3 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
* Re: [PATCH] Fix not ignoring case of uuid given to RegisterEndpoint
From: Johan Hedberg @ 2010-11-04 18:17 UTC (permalink / raw)
To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1288880990-2125-1-git-send-email-luiz.dentz@gmail.com>
Hi Luiz,
On Thu, Nov 04, 2010, Luiz Augusto von Dentz wrote:
> From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
>
> ---
> audio/media.c | 6 +++---
> audio/transport.c | 8 ++++----
> 2 files changed, 7 insertions(+), 7 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
* Re: [PATCH] Fix add disconnect watch in connecting state
From: Johan Hedberg @ 2010-11-04 18:16 UTC (permalink / raw)
To: Daniel Örstadius; +Cc: linux-bluetooth
In-Reply-To: <AANLkTikyPtikmH=ep2va2iLke9NoteTr5wpThfR6v=vf@mail.gmail.com>
Hi Daniel,
On Thu, Nov 04, 2010, Daniel Örstadius wrote:
> If disconnect_cb is added only in the connected state, the callback
> will not be triggered if Device.Disconnected is called during a
> connection setup and the RFCOMM channel of HFP will not be cleanly
> disconnected.
> ---
> audio/device.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
>
> diff --git a/audio/device.c b/audio/device.c
> index 9554c7b..2e75538 100644
> --- a/audio/device.c
> +++ b/audio/device.c
> @@ -231,7 +231,7 @@ static void device_set_state(struct audio_device *dev, audio_state_t new_state)
> priv->dc_id);
> priv->dc_id = 0;
> }
> - } else if (new_state == AUDIO_STATE_CONNECTED)
> + } else
> priv->dc_id = device_add_disconnect_watch(dev->btd_dev,
> disconnect_cb, dev, NULL);
>
This doesn't seem quite right to me. Aren't you now adding a disconnect
callback twice: once for CONNECTING and a second time for CONNECTED? It
seems like you should only add it when new_state == CONNECTING, right?
Johan
^ permalink raw reply
* Re: [PATCH] Fix status problem after pulling empty call history
From: Johan Hedberg @ 2010-11-04 18:11 UTC (permalink / raw)
To: Rafal Michalski; +Cc: linux-bluetooth
In-Reply-To: <1288871880-5857-1-git-send-email-michalski.raf@gmail.com>
Hi Rafal,
On Thu, Nov 04, 2010, Rafal Michalski wrote:
> Problem with "OK" status after pulling phonebook for empty call history
> (cch) is fixed by this patch. Previously "OK" status was received even
> for empty (as it normally does for non-empty) call history and now
> "Not Found" status is received in case when there is no call event.
> ---
> plugins/pbap.c | 2 +-
> 1 files changed, 1 insertions(+), 1 deletions(-)
Pushed upstream. Thanks.
Johan
^ permalink raw reply
* [PATCH] Fix handling call waiting indicators in telephony
From: Lukasz Pawlik @ 2010-11-04 15:00 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Lukasz Pawlik
Previously +CIEV: "callsetup" was sent before +CCWA: (call waiting)
notification. According to the HFP specification AG reports new
call waiting by sending +CCWA before +CIEV indicator. Some HF did
not handle properly ending active call after rejecting 2nd incoming call
if "callsetup" indicator was sent before call waiting.
---
audio/telephony-maemo6.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/audio/telephony-maemo6.c b/audio/telephony-maemo6.c
index 33fd13f..72c8e36 100644
--- a/audio/telephony-maemo6.c
+++ b/audio/telephony-maemo6.c
@@ -914,9 +914,6 @@ static void handle_incoming_call(DBusMessage *msg)
g_free(call->number);
call->number = g_strdup(number);
- telephony_update_indicator(maemo_indicators, "callsetup",
- EV_CALLSETUP_INCOMING);
-
if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
find_call_with_status(CSD_CALL_STATUS_HOLD))
telephony_call_waiting_ind(call->number,
@@ -924,6 +921,9 @@ static void handle_incoming_call(DBusMessage *msg)
else
telephony_incoming_call_ind(call->number,
number_type(call->number));
+
+ telephony_update_indicator(maemo_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
}
static void handle_outgoing_call(DBusMessage *msg)
--
1.7.0.4
^ permalink raw reply related
* Re: [PATCH] Fix Maemo6 MCE: set state variable directly
From: Johan Hedberg @ 2010-11-04 14:41 UTC (permalink / raw)
To: Daniel Örstadius; +Cc: linux-bluetooth
In-Reply-To: <AANLkTimpjr1Kc23zn=7uf0WNV5784ccPq8hWtBYrNADh@mail.gmail.com>
On Wed, Oct 20, 2010, Daniel Örstadius wrote:
> From: Daniel Orstadius <daniel.orstadius@nokia.com>
> Date: Wed, 20 Oct 2010 17:13:17 +0300
> Subject: [PATCH] Fix Maemo6 MCE: set state variable directly
>
> If the state variable is not updated until mce_signal_callback and
> SetProperty(Powered) is called two times quickly, then the second
> call will not generate a request to change the radio state of the
> MCE if its corresponding call to adapter_powered occurs before the
> first triggering of mce_signal_callback.
>
> With this patch two calls to the MCE are executed, although there
> might still be an unnecessary power cycle in that
> mce_signal_callback is also triggered twice.
> ---
> plugins/maemo6.c | 4 +++-
> 1 files changed, 3 insertions(+), 1 deletions(-)
This patch has been pushed upstream after an offline discussion which
concluded that it should be safe to ignore the method reply from MCE in
this case.
Johan
^ permalink raw reply
* [PATCH] Fix not ignoring case of uuid given to RegisterEndpoint
From: Luiz Augusto von Dentz @ 2010-11-04 14:29 UTC (permalink / raw)
To: linux-bluetooth
From: Luiz Augusto von Dentz <luiz.dentz-von@nokia.com>
---
audio/media.c | 6 +++---
audio/transport.c | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/audio/media.c b/audio/media.c
index 661f81a..b6c90f9 100644
--- a/audio/media.c
+++ b/audio/media.c
@@ -190,19 +190,19 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
endpoint->size = size;
endpoint->adapter = adapter;
- if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0) {
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
endpoint->sep = a2dp_add_sep(&adapter->src,
AVDTP_SEP_TYPE_SOURCE, codec,
delay_reporting, endpoint);
if (endpoint->sep == NULL)
goto failed;
- } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
endpoint->sep = a2dp_add_sep(&adapter->src,
AVDTP_SEP_TYPE_SINK, codec,
delay_reporting, endpoint);
if (endpoint->sep == NULL)
goto failed;
- } else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
g_strcmp0(uuid, HSP_AG_UUID) == 0)
endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
endpoint);
diff --git a/audio/transport.c b/audio/transport.c
index 6d78b73..eda46e1 100644
--- a/audio/transport.c
+++ b/audio/transport.c
@@ -711,15 +711,15 @@ struct media_transport *media_transport_create(DBusConnection *conn,
transport->fd = -1;
uuid = media_endpoint_get_uuid(endpoint);
- if (g_strcmp0(uuid, A2DP_SOURCE_UUID) == 0 ||
- g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
+ strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
transport->resume = resume_a2dp;
transport->suspend = suspend_a2dp;
transport->cancel = cancel_a2dp;
transport->get_properties = get_properties_a2dp;
transport->set_property = set_property_a2dp;
- } else if (g_strcmp0(uuid, HFP_AG_UUID) == 0 ||
- g_strcmp0(uuid, HSP_AG_UUID) == 0) {
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+ strcasecmp(uuid, HSP_AG_UUID) == 0) {
transport->resume = resume_headset;
transport->suspend = suspend_headset;
transport->cancel = cancel_headset;
--
1.7.1
^ permalink raw reply related
* [PATCH v3] Add support for sending large PBAP response in many parts
From: Radoslaw Jablonski @ 2010-11-04 14:18 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Radoslaw Jablonski
Added file buffer to cache pull results - temporary file will be deleted
when response is sent. Also added partial_resp variable to pbap_session
for holding information if more data will be available from source later.
It was needed to know when sent -EAGAIN to obex, if currently is no data
available in the buffer.
---
plugins/pbap.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 79 insertions(+), 10 deletions(-)
diff --git a/plugins/pbap.c b/plugins/pbap.c
index 3ea7d6b..6742ea7 100644
--- a/plugins/pbap.c
+++ b/plugins/pbap.c
@@ -116,6 +116,8 @@
</attribute> \
</record>"
+#define PBAP_BUF_TEMPLATE "pbap_pullXXXXXX"
+
struct aparam_header {
uint8_t tag;
uint8_t len;
@@ -143,6 +145,10 @@ struct pbap_session {
uint32_t find_handle;
GString *buffer;
struct cache cache;
+ gboolean partial_resp;
+ int fbuf_w;
+ int fbuf_r;
+ char *buf_path;
};
static const uint8_t PBAP_TARGET[TARGET_SIZE] = {
@@ -256,11 +262,27 @@ static void query_result(const char *buffer, size_t bufsize, int vcards,
return;
}
- if (!pbap->buffer)
- pbap->buffer = g_string_new_len(buffer, bufsize);
- else
- pbap->buffer = g_string_append_len(pbap->buffer, buffer,
- bufsize);
+ if (pbap->fbuf_w < 0) {
+ /* Creating file buffer for results*/
+ pbap->buf_path = g_build_filename(g_get_tmp_dir(),
+ PBAP_BUF_TEMPLATE, NULL);
+ pbap->fbuf_w = g_mkstemp(pbap->buf_path);
+
+ if (pbap->fbuf_w < 0)
+ return -EPERM;
+ }
+
+ write(pbap->fbuf_w, buffer, bufsize);
+
+ /* If partial_resp will be set to TRUE, then we won't end transmission
+ * after sending one part of results to the client via obex*/
+ pbap->partial_resp = missed ? TRUE : FALSE;
+
+ /* If no more data in future, we close file buffer right now*/
+ if (!pbap->partial_resp) {
+ close(pbap->fbuf_w);
+ pbap->fbuf_w = -1;
+ }
obex_object_set_io_flags(pbap, G_IO_IN, 0);
}
@@ -549,6 +571,8 @@ static void *pbap_connect(struct obex_session *os, int *err)
pbap = g_new0(struct pbap_session, 1);
pbap->folder = g_strdup("/");
pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
+ pbap->fbuf_r = -1;
+ pbap->fbuf_w = -1;
if (err)
*err = 0;
@@ -829,6 +853,27 @@ fail:
return NULL;
}
+static ssize_t pbap_read_fbuf(struct pbap_session *pbap, void *buf,
+ size_t count)
+{
+ ssize_t len;
+
+ if (pbap->fbuf_r < 0) {
+ pbap->fbuf_r = open(pbap->buf_path, 0);
+
+ if(pbap->fbuf_r < 0)
+ return -EPERM;
+ }
+
+ len = read(pbap->fbuf_r, buf, count);
+
+ if (len == 0 && pbap->partial_resp)
+ /* More data available later */
+ return -EAGAIN;
+ else
+ return len;
+}
+
static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
uint8_t *hi)
{
@@ -837,17 +882,23 @@ static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
DBG("buffer %p maxlistcount %d", pbap->buffer,
pbap->params->maxlistcount);
- if (!pbap->buffer)
+ if (!pbap->buf_path && !pbap->buffer)
+ /* No response available now (no memory/file buffer exist)*/
return -EAGAIN;
- /* PhoneBookSize */
- if (pbap->params->maxlistcount == 0)
+ /* Result from pb size query is very short (only number) so it makes no
+ * sense to create file buffer for it - using memory buff */
+ if (pbap->params->maxlistcount == 0) {
+ /* PhoneBookSize */
*hi = OBEX_HDR_APPARAM;
- else
+
+ return string_read(pbap->buffer, buf, count);
+ } else {
/* Stream data */
*hi = OBEX_HDR_BODY;
- return string_read(pbap->buffer, buf, count);
+ return pbap_read_fbuf(pbap, buf, count);
+ }
}
static ssize_t vobject_list_read(void *object, void *buf, size_t count,
@@ -893,6 +944,24 @@ static int vobject_close(void *object)
pbap->buffer = NULL;
}
+ if (pbap->fbuf_r >= 0) {
+ close(pbap->fbuf_r);
+ pbap->fbuf_r = -1;
+ }
+
+ if (pbap->fbuf_w >= 0) {
+ close(pbap->fbuf_w);
+ pbap->fbuf_w = -1;
+ }
+
+ if (pbap->buf_path) {
+ /* remove file buffer for pull queries */
+ unlink(pbap->buf_path);
+
+ g_free(pbap->buf_path);
+ pbap->buf_path = NULL;
+ }
+
return 0;
}
--
1.7.0.4
^ permalink raw reply related
* [PATCH] Fix add disconnect watch in connecting state
From: Daniel Örstadius @ 2010-11-04 13:20 UTC (permalink / raw)
To: linux-bluetooth
[-- Attachment #1: Type: text/plain, Size: 1 bytes --]
[-- Attachment #2: 0001-Fix-add-disconnect-watch-in-connecting-state.patch --]
[-- Type: text/x-patch, Size: 948 bytes --]
From f705532320bdfb47a06df72c6d031ca096834a3a Mon Sep 17 00:00:00 2001
From: Daniel Orstadius <daniel.orstadius@nokia.com>
Date: Wed, 3 Nov 2010 14:12:29 +0200
Subject: [PATCH] Fix add disconnect watch in connecting state
If disconnect_cb is added only in the connected state, the callback
will not be triggered if Device.Disconnected is called during a
connection setup and the RFCOMM channel of HFP will not be cleanly
disconnected.
---
audio/device.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/audio/device.c b/audio/device.c
index 9554c7b..2e75538 100644
--- a/audio/device.c
+++ b/audio/device.c
@@ -231,7 +231,7 @@ static void device_set_state(struct audio_device *dev, audio_state_t new_state)
priv->dc_id);
priv->dc_id = 0;
}
- } else if (new_state == AUDIO_STATE_CONNECTED)
+ } else
priv->dc_id = device_add_disconnect_watch(dev->btd_dev,
disconnect_cb, dev, NULL);
--
1.6.0.4
^ permalink raw reply related
* HDP stress test script
From: Elvis Pfützenreuter @ 2010-11-04 12:59 UTC (permalink / raw)
To: Jose Antonio Santos Cadenas; +Cc: linux-bluetooth
I put together a kind of "stress test" or "torture" script for HDP profile and API. It is available at http://gitorious.org/hdpy project, path is scripts/bluez/hdp_stress_test. Major features:
a) useful either as a test tool and as an API usage example (though scripts/bluez/hdp_oxi_sink is probably a better example);
b) any number of machines can participate (minimum 2, tested with 3);
c) each machine finds the others semi-automatically (tries to contact paired devices that publish an HDP record).
I could extract a segfault with it already, but did not have time to pinpoint the cause. It happened when there were a couple of async Echo's pending and script was killed (which triggers HealthApplication destruction).
Note: it uses the 'Application' property of HealthChannel, which is pending acceptance. This can be easily disabled by changing a flag in script.
Another thing I stumbled on, was the UUIDs property of Device object. The script creates sources and sinks, but 'UUIDs' only shows the sink (0x1402). For now, the script assumes that every source is also a sink in order to find the counterparts, but this needs to be truly fixed in near future.
^ permalink raw reply
* Re: [PATCH v2] Add support for sending large PBAP response in many parts
From: Luiz Augusto von Dentz @ 2010-11-04 12:20 UTC (permalink / raw)
To: Radoslaw Jablonski; +Cc: linux-bluetooth
In-Reply-To: <1288860932-9513-2-git-send-email-ext-jablonski.radoslaw@nokia.com>
Hi,
On Thu, Nov 4, 2010 at 10:55 AM, Radoslaw Jablonski
<ext-jablonski.radoslaw@nokia.com> wrote:
> Added file buffer to cache pull results - temporary file will be deleted
> when response is sent. Also added partial_resp variable to pbap_session
> for holding information if more data will be available from source later.
> It was needed to know when sent -EAGAIN to obex, if currently is no data
> available in the buffer.
> ---
> plugins/pbap.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++-------
> 1 files changed, 72 insertions(+), 10 deletions(-)
>
> diff --git a/plugins/pbap.c b/plugins/pbap.c
> index 3ea7d6b..e59ce8d 100644
> --- a/plugins/pbap.c
> +++ b/plugins/pbap.c
> @@ -116,6 +116,8 @@
> </attribute> \
> </record>"
>
> +#define PBAP_BUF_TEMPLATE "pbap_pullXXXXXX"
> +
> struct aparam_header {
> uint8_t tag;
> uint8_t len;
> @@ -143,6 +145,10 @@ struct pbap_session {
> uint32_t find_handle;
> GString *buffer;
> struct cache cache;
> + gboolean partial_resp;
> + int fbuf_w;
> + int fbuf_r;
> + char *buf_path;
> };
>
> static const uint8_t PBAP_TARGET[TARGET_SIZE] = {
> @@ -256,13 +262,28 @@ static void query_result(const char *buffer, size_t bufsize, int vcards,
> return;
> }
>
> - if (!pbap->buffer)
> - pbap->buffer = g_string_new_len(buffer, bufsize);
> - else
> - pbap->buffer = g_string_append_len(pbap->buffer, buffer,
> - bufsize);
> + if (!pbap->fbuf_w) {
> + /* Creating file buffer for results*/
> + pbap->buf_path = g_build_filename(g_get_tmp_dir(),
> + PBAP_BUF_TEMPLATE, NULL);
> + pbap->fbuf_w = g_mkstemp(pbap->buf_path);
> +
> + if (pbap->fbuf_w < 0)
> + return -EPERM;
> + }
When checking if fd is valid doing < 0
> +
> + write(pbap->fbuf_w, buffer, bufsize);
> +
> + /* If partial_resp will be set to TRUE, then we won't end transmission
> + * after sending one part of results to the client via obex*/
> + pbap->partial_resp = missed ? TRUE : FALSE;
> +
> + /* If no more data in future, we close file buffer right now*/
> + if(!pbap->partial_resp)
> + close(pbap->fbuf_w);
When closing the fd set it to -1.
> obex_object_set_io_flags(pbap, G_IO_IN, 0);
> + DBG("Query result end...");
> }
>
> static void cache_entry_notify(const char *id, uint32_t handle,
> @@ -829,6 +850,27 @@ fail:
> return NULL;
> }
>
> +static ssize_t pbap_read_fbuf(struct pbap_session *pbap, void *buf,
> + size_t count)
> +{
> + ssize_t len;
> +
> + if (!pbap->fbuf_r) {
> + pbap->fbuf_r = open(pbap->buf_path, 0);
> +
> + if(pbap->fbuf_r < 0)
> + return -EPERM;
> + }
Again use negative values to check if fd was already opened.
> + len = read(pbap->fbuf_r, buf, count);
> +
> + if (len == 0 && pbap->partial_resp)
> + /* More data available later */
> + return -EAGAIN;
> + else
> + return len;
> +}
> +
> static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
> uint8_t *hi)
> {
> @@ -837,17 +879,23 @@ static ssize_t vobject_pull_read(void *object, void *buf, size_t count,
> DBG("buffer %p maxlistcount %d", pbap->buffer,
> pbap->params->maxlistcount);
>
> - if (!pbap->buffer)
> + if (!pbap->fbuf_w && !pbap->buffer)
> + /* No response in buffers now */
> return -EAGAIN;
Again use negative values.
> - /* PhoneBookSize */
> - if (pbap->params->maxlistcount == 0)
> + /* Result from pb size query is very short (only number) so it makes no
> + * sense to create file buffer for it - using memory buff */
> + if (pbap->params->maxlistcount == 0) {
> + /* PhoneBookSize */
> *hi = OBEX_HDR_APPARAM;
> - else
> +
> + return string_read(pbap->buffer, buf, count);
> + } else {
> /* Stream data */
> *hi = OBEX_HDR_BODY;
>
> - return string_read(pbap->buffer, buf, count);
> + return pbap_read_fbuf(pbap, buf, count);
> + }
> }
>
> static ssize_t vobject_list_read(void *object, void *buf, size_t count,
> @@ -893,6 +941,20 @@ static int vobject_close(void *object)
> pbap->buffer = NULL;
> }
>
> + if (pbap->fbuf_r)
> + close(pbap->fbuf_r);
> +
> + if (pbap->fbuf_w)
> + close(pbap->fbuf_w);
When you close a fd reset it to -1.
> + if (pbap->buf_path) {
> + /* remove file buffer for pull queries */
> + unlink(pbap->buf_path);
> +
> + g_free(pbap->buf_path);
> + pbap->buf_path = NULL;
> + }
> +
> return 0;
> }
--
Luiz Augusto von Dentz
Computer Engineer
^ permalink raw reply
* [PATCH] Fix status problem after pulling empty call history
From: Rafal Michalski @ 2010-11-04 11:58 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Rafal Michalski
Problem with "OK" status after pulling phonebook for empty call history
(cch) is fixed by this patch. Previously "OK" status was received even
for empty (as it normally does for non-empty) call history and now
"Not Found" status is received in case when there is no call event.
---
plugins/pbap.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/plugins/pbap.c b/plugins/pbap.c
index 3ea7d6b..a40563c 100644
--- a/plugins/pbap.c
+++ b/plugins/pbap.c
@@ -251,7 +251,7 @@ static void query_result(const char *buffer, size_t bufsize, int vcards,
DBG("");
- if (vcards < 0) {
+ if (vcards <= 0) {
obex_object_set_io_flags(pbap, G_IO_ERR, -ENOENT);
return;
}
--
1.6.3.3
^ permalink raw reply related
* [PATCH 4/4] Add simple-oobprovider for testing.
From: Szymon Janc @ 2010-11-04 10:11 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1288865461-3760-1-git-send-email-szymon.janc@tieto.com>
---
test/simple-oobprovider | 54 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 54 insertions(+), 0 deletions(-)
create mode 100755 test/simple-oobprovider
diff --git a/test/simple-oobprovider b/test/simple-oobprovider
new file mode 100755
index 0000000..135f0f7
--- /dev/null
+++ b/test/simple-oobprovider
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+# Copyright (C) 2010 ST-Ericsson SA
+# Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class Provider(dbus.service.Object):
+
+ remotedata = None
+
+ @dbus.service.method("org.bluez.OOB",
+ in_signature="s", out_signature="ayay")
+
+ def RequestRemoteOobData(self, address):
+ print "RequestRemoteOobData for %s" % (address)
+ return self.remotedata
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+ adapter = dbus.Interface(bus.get_object("org.bluez",
+ manager.DefaultAdapter()), "org.bluez.Adapter")
+ adr = adapter.GetProperties()['Address']
+
+ oob = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"),
+ "org.bluez.OOB")
+
+ path = "/test/oobprovider"
+ provider = Provider(bus, path)
+
+ mainloop = gobject.MainLoop()
+
+ oob.RegisterProvider(path)
+
+ print "Local data for %s:" % (adr)
+ print oob.UpdateLocalOobData(adr)
+
+ provider.remotedata = input("Provide remote data (in python syntax):\n")
+
+ print "You may try pairing now"
+
+ mainloop.run()
+
+ #adapter.UnregisterProvider(path)
+ #print "Provider unregistered"
--
1.7.1
^ permalink raw reply related
* [PATCH 3/4] Add DBus OOB API documentation.
From: Szymon Janc @ 2010-11-04 10:11 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1288865461-3760-1-git-send-email-szymon.janc@tieto.com>
---
Makefile.am | 3 +-
doc/oob-api.txt | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+), 1 deletions(-)
create mode 100644 doc/oob-api.txt
diff --git a/Makefile.am b/Makefile.am
index d6cbf92..b5157cd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -358,7 +358,8 @@ EXTRA_DIST += doc/manager-api.txt \
doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
doc/serial-api.txt doc/network-api.txt \
doc/input-api.txt doc/audio-api.txt doc/control-api.txt \
- doc/hfp-api.txt doc/assigned-numbers.txt
+ doc/hfp-api.txt doc/assigned-numbers.txt doc/oob-api.txt
+
AM_YFLAGS = -d
diff --git a/doc/oob-api.txt b/doc/oob-api.txt
new file mode 100644
index 0000000..fce18a7
--- /dev/null
+++ b/doc/oob-api.txt
@@ -0,0 +1,62 @@
+BlueZ D-Bus OOB API description
+*******************************
+
+Copyright (C) 2010 ST-Ericsson SA
+
+Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+OOB hierarchy
+=================
+
+Service unique name
+Interface org.bluez.OOB
+Object path freely definable
+
+Methods array{bye}, array{byte} RequestRemoteOobData(string address)
+
+ This method gets called when the service daemon needs to
+ get hash and randomizer for an OOB authentication.
+
+ The return value should be pair of arrays of 16 bytes
+ each. First hash, second randomizer.
+
+ If no OOB data is present for specified address empty
+ reply should be returned.
+
+ void Deactivate()
+
+ This method gets called when DBus plug-in for OOB was
+ deactivated. There is no need to unregister provider,
+ because when this method gets called it has already been
+ unregistered.
+
+--------------------------------------------------------------------------------
+
+Service org.bluez
+Interface org.bluez.OOB
+Object path /org/bluez
+
+ void RegisterProvider(object provider)
+
+ This method registers provider for DBus OOB plug-in.
+ When provider is successfully registered plug-in becomes
+ active. Only one provider can be registered at time.
+
+ Possible errors: org.bluez.Error.AlreadyExists
+
+ void UnregisterProvider(object provider)
+
+ This method unregisters provider for DBus OOB plug-in.
+ When provider is successfully unregistered plug-in
+ becomes inactive and will emit Deactivated() signal.
+
+ Possible errors: org.bluez.Error.DoesNotExist
+
+ array{bye}, array{byte} UpdateLocalOobData(string address)
+
+ This method generates new local OOB data for specified
+ address (adapter). Return value is pair of arrays 16
+ bytes each. First hash, second randomizer. Only
+ registered provider should call this method.
+
+ Possible errors: org.bluez.Error.UpdateFailed
--
1.7.1
^ permalink raw reply related
* [PATCH 2/4] Add DBus OOB plugin.
From: Szymon Janc @ 2010-11-04 10:10 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1288865461-3760-1-git-send-email-szymon.janc@tieto.com>
---
Makefile.am | 5 +
acinclude.m4 | 6 +
plugins/dbusoob.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 367 insertions(+), 0 deletions(-)
create mode 100644 plugins/dbusoob.c
diff --git a/Makefile.am b/Makefile.am
index 1b71cc4..d6cbf92 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -216,6 +216,11 @@ builtin_modules += maemo6
builtin_sources += plugins/maemo6.c
endif
+if DBUSOOBPLUGIN
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c
+endif
+
sbin_PROGRAMS += src/bluetoothd
src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
diff --git a/acinclude.m4 b/acinclude.m4
index 287f07d..a52d063 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -193,6 +193,7 @@ AC_DEFUN([AC_ARG_BLUEZ], [
configfiles_enable=yes
telephony_driver=dummy
maemo6_enable=no
+ dbusoob_enable=no
AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
optimization_enable=${enableval}
@@ -316,6 +317,10 @@ AC_DEFUN([AC_ARG_BLUEZ], [
maemo6_enable=${enableval}
])
+ AC_ARG_ENABLE(dbusoob, AC_HELP_STRING([--enable-dbusoob], [compile with DBUS OOB plugin]), [
+ dbusoob_enable=${enableval}
+ ])
+
AC_ARG_ENABLE(hal, AC_HELP_STRING([--enable-hal], [Use HAL to determine adapter class]), [
hal_enable=${enableval}
])
@@ -372,4 +377,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes")
+ AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
])
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
new file mode 100644
index 0000000..335de76
--- /dev/null
+++ b/plugins/dbusoob.c
@@ -0,0 +1,356 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "manager.h"
+#include "device.h"
+#include "adapter.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "oob.h"
+
+#define OOB_INTERFACE "org.bluez.OOB"
+#define OOB_PATH "/org/bluez"
+
+struct oob_provider {
+ char *name;
+ char *path;
+
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+
+ guint listener_id;
+ gboolean exited;
+};
+
+static struct oob_provider *provider = NULL;
+static DBusConnection *connection = NULL;
+static struct oob_plugin dbusoob;
+
+static void destroy_provider(void)
+{
+ if (!provider->exited)
+ g_dbus_remove_watch(connection, provider->listener_id);
+
+ if (provider->msg)
+ dbus_message_unref(provider->msg);
+
+ g_free(provider->name);
+ g_free(provider->path);
+ g_free(provider);
+ provider = NULL;
+
+ oob_deactivate_plugin(&dbusoob);
+}
+
+static void provider_exited(DBusConnection *conn, void *user_data)
+{
+ DBG("Provider exited without calling Unregister");
+
+ provider->exited = TRUE;
+ destroy_provider();
+}
+
+static void create_provider(const char *path, const char *name)
+{
+ provider = g_new(struct oob_provider, 1);
+ provider->path = g_strdup(path);
+ provider->name = g_strdup(name);
+ provider->adapter = NULL;
+ provider->msg = NULL;
+ provider->exited = FALSE;
+ provider->listener_id = g_dbus_add_disconnect_watch(connection, name,
+ provider_exited, NULL, NULL);
+
+ oob_activate_plugin(&dbusoob);
+}
+
+static void request_remote_data_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *msg;
+ DBusError err;
+ struct btd_device *device = user_data;
+ uint8_t *hash = NULL;
+ uint8_t *randomizer = NULL;
+ int32_t hash_len;
+ int32_t rand_len;
+
+ msg = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, msg)) {
+ error("Provider replied with an error: %s, %s", err.name,
+ err.message);
+ dbus_error_free(&err);
+ goto error;
+ }
+
+ dbus_error_init(&err);
+ if (!dbus_message_get_args(msg, &err,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hash_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &rand_len,
+ DBUS_TYPE_INVALID)
+ || hash_len != 16 || rand_len != 16) {
+ error("RequestRemoteOobData reply signature error: %s, %s",
+ err.name, err.message);
+ dbus_error_free(&err);
+ hash = NULL;
+ randomizer = NULL;
+ }
+
+error:
+ dbus_message_unref(msg);
+ dbus_pending_call_unref(call);
+
+ device_set_oob_data(device, hash, randomizer);
+}
+
+static gboolean request_remote_data(struct btd_device *device)
+{
+ DBusMessage* msg;
+ DBusPendingCall *call = NULL;
+ bdaddr_t ba;
+ char addr[18];
+ const char *paddr = addr;
+ gboolean ret = FALSE;
+
+ msg = dbus_message_new_method_call(provider->name, provider->path,
+ OOB_INTERFACE, "RequestRemoteOobData");
+
+ if (!msg) {
+ error("Couldn't allocate D-Bus message");
+ goto error;
+ }
+
+ device_get_address(device, &ba);
+ ba2str(&ba, addr);
+
+ if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID)) {
+ error ("Couldn't append arguments");
+ goto error;
+ }
+
+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+ error("D-Bus send failed");
+ goto error;
+ }
+
+ if (!dbus_pending_call_set_notify(call, request_remote_data_reply,
+ device, NULL)) {
+ error("Couldn't set reply notification.");
+ dbus_pending_call_unref(call);
+ goto error;
+ }
+
+ ret = TRUE;
+
+error:
+ if (msg)
+ dbus_message_unref(msg);
+
+ return ret;
+}
+
+static void local_data_updated(bdaddr_t *ba, uint8_t *hash, uint8_t *randomizer)
+{
+ struct DBusMessage *reply;
+ bdaddr_t addr;
+
+ if (!provider)
+ return;
+
+ adapter_get_address(provider->adapter, &addr);
+ if (bacmp(ba, &addr))
+ return;
+
+ if (hash && randomizer)
+ reply = g_dbus_create_reply(provider->msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, 16,
+ DBUS_TYPE_INVALID);
+ else
+ reply = g_dbus_create_error(provider->msg,
+ ERROR_INTERFACE ".UpdateFailed",
+ "Failed to update local OOB.");
+
+ dbus_message_unref(provider->msg);
+ provider->msg = NULL;
+ provider->adapter = NULL;
+
+ if (!reply) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ if (!g_dbus_send_message(connection, reply))
+ error("D-Bus send failed");
+
+}
+
+static DBusMessage *update_local_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *name;
+ const char *addr;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ name = dbus_message_get_sender(msg);
+ if (!provider || provider->name != name)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".UpdateFailed",
+ "Not a OOB provider or no provider registered");
+
+ if (provider->msg)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".UpdateFailed",
+ "Another request in progress.");
+
+ provider->adapter = manager_find_adapter_by_address(addr);
+ if (!provider->adapter)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".UpdateFailed",
+ "No adapter with given address found");
+
+ if (btd_adapter_read_local_oob_data(provider->adapter))
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".UpdateFailed",
+ "HCI request failed");
+
+ provider->msg = dbus_message_ref(msg);
+ return NULL;
+}
+
+static void plugin_deactivated(void)
+{
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(provider->name, provider->path,
+ OOB_INTERFACE, "Deactivate");
+
+ if (!msg)
+ error("Couldn't allocate D-Bus message");
+ else if (!g_dbus_send_message(connection, msg))
+ error("D-Bus send failed");
+
+ destroy_provider();
+}
+
+static DBusMessage *register_provider(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *path;
+ const char *name;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ if (provider)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+ "OOB provider already registered");
+
+ name = dbus_message_get_sender(msg);
+ create_provider(path, name);
+
+ DBG("OOB provider registered at %s:%s", provider->name, provider->path);
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_provider(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *path;
+ const char *name;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ name = dbus_message_get_sender(msg);
+
+ if (!provider || !g_str_equal(provider->path, path)
+ || !g_str_equal(provider->name, name))
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist",
+ "No such OOB provider registered");
+
+ DBG("OOB provider (%s:%s) unregistered", provider->name, provider->path);
+
+ destroy_provider();
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable oob_methods[] = {
+ { "RegisterProvider", "o", "", register_provider},
+ { "UnregisterProvider", "o", "", unregister_provider},
+ { "UpdateLocalOobData", "s", "ayay", update_local_data, G_DBUS_METHOD_FLAG_ASYNC},
+ { }
+};
+
+static gboolean register_on_dbus(void)
+{
+ connection = get_dbus_connection();
+
+ if (!g_dbus_register_interface(connection, OOB_PATH, OOB_INTERFACE,
+ oob_methods, NULL, NULL, NULL, NULL)) {
+ error("OOB interface init failed on path %s", OOB_PATH);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int dbusoob_init(void)
+{
+ DBG("Setup dbusoob plugin");
+
+ dbusoob.request_remote_data = request_remote_data;
+ dbusoob.local_data_updated = local_data_updated;
+ dbusoob.plugin_deactivated = plugin_deactivated;
+
+ return register_on_dbus();
+}
+
+static void dbusoob_exit(void)
+{
+ DBG("Cleanup dbusoob plugin");
+ oob_deactivate_plugin(&dbusoob);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, dbusoob_init, dbusoob_exit)
--
1.7.1
^ permalink raw reply related
* [PATCH 1/4] Add support for Out of Band (OOB) association model.
From: Szymon Janc @ 2010-11-04 10:10 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1288865461-3760-1-git-send-email-szymon.janc@tieto.com>
---
Makefile.am | 3 +-
lib/hci.h | 3 ++
plugins/hciops.c | 70 +++++++++++++++++++++++++++++++++++++++--------
src/adapter.c | 5 +++
src/adapter.h | 3 ++
src/device.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/device.h | 12 ++++++++
src/event.c | 73 +++++++++++++++++++++++++++++++++++++------------
src/event.h | 3 +-
src/oob.c | 61 +++++++++++++++++++++++++++++++++++++++++
src/oob.h | 47 +++++++++++++++++++++++++++++++
11 files changed, 326 insertions(+), 34 deletions(-)
create mode 100644 src/oob.c
create mode 100644 src/oob.h
diff --git a/Makefile.am b/Makefile.am
index 873f2df..1b71cc4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -238,7 +238,8 @@ src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
src/adapter.h src/adapter.c \
src/device.h src/device.c \
src/dbus-common.c src/dbus-common.h \
- src/event.h src/event.c
+ src/event.h src/event.c \
+ src/oob.c
src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
@CAPNG_LIBS@ -ldl -lrt
src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
diff --git a/lib/hci.h b/lib/hci.h
index 0cb120f..409abd9 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -524,6 +524,9 @@ typedef struct {
#define OCF_REMOTE_OOB_DATA_NEG_REPLY 0x0033
+#define OOB_DATA_NOT_PRESENT 0x00
+#define OOB_DATA_PRESENT 0x01
+
#define OCF_IO_CAPABILITY_NEG_REPLY 0x0034
typedef struct {
bdaddr_t bdaddr;
diff --git a/plugins/hciops.c b/plugins/hciops.c
index 8a79010..d8a1d2c 100644
--- a/plugins/hciops.c
+++ b/plugins/hciops.c
@@ -3,6 +3,7 @@
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2010 ST-Ericsson SA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,6 +48,7 @@
#include "event.h"
#include "device.h"
#include "manager.h"
+#include "oob.h"
#define HCI_REQ_TIMEOUT 5000
@@ -504,33 +506,45 @@ static void user_passkey_notify(int dev, bdaddr_t *sba, void *ptr)
static void remote_oob_data_request(int dev, bdaddr_t *sba, void *ptr)
{
- hci_send_cmd(dev, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, ptr);
+ bdaddr_t *dba = ptr;
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ char da[18];
+
+ ba2str(dba, da);
+ adapter = manager_find_adapter(sba);
+ device = adapter_find_device(adapter, da);
+
+ if (device_has_oob_data(device)) {
+ remote_oob_data_reply_cp cp;
+
+ bacpy(&cp.bdaddr, dba);
+ device_get_oob_data(device,cp.hash,cp.randomizer);
+
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
+ REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
+ } else
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_NEG_REPLY,
+ 6, ptr);
}
static void io_capa_request(int dev, bdaddr_t *sba, bdaddr_t *dba)
{
char sa[18], da[18];
- uint8_t cap, auth;
ba2str(sba, sa); ba2str(dba, da);
info("io_capa_request (sba=%s, dba=%s)", sa, da);
- if (btd_event_get_io_cap(sba, dba, &cap, &auth) < 0) {
+ /* If failed to establish IO capabilities then send negative reply
+ * immediately. Positive reply will be sent when IO capabilities are
+ * established. */
+ if (btd_event_request_io_cap(sba, dba)) {
io_capability_neg_reply_cp cp;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, dba);
cp.reason = HCI_PAIRING_NOT_ALLOWED;
hci_send_cmd(dev, OGF_LINK_CTL, OCF_IO_CAPABILITY_NEG_REPLY,
IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
- } else {
- io_capability_reply_cp cp;
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, dba);
- cp.capability = cap;
- cp.oob_data = 0x00;
- cp.authentication = auth;
- hci_send_cmd(dev, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
- IO_CAPABILITY_REPLY_CP_SIZE, &cp);
}
}
@@ -731,6 +745,15 @@ static void read_scan_complete(bdaddr_t *sba, uint8_t status, void *ptr)
adapter_mode_changed(adapter, rp->enable);
}
+static void read_local_oob_data_complete(bdaddr_t *local, uint8_t status,
+ read_local_oob_data_rp *rp)
+{
+ if (status)
+ oob_local_data_updated(local, NULL, NULL);
+ else
+ oob_local_data_updated(local, rp->hash, rp->randomizer);
+}
+
static inline void cmd_complete(int dev, bdaddr_t *sba, void *ptr)
{
evt_cmd_complete *evt = ptr;
@@ -785,6 +808,10 @@ static inline void cmd_complete(int dev, bdaddr_t *sba, void *ptr)
ptr += sizeof(evt_cmd_complete);
adapter_update_tx_power(sba, status, ptr);
break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_oob_data_complete(sba, status, ptr);
+ break;
};
}
@@ -2289,6 +2316,24 @@ static int hciops_get_remote_version(int index, uint16_t handle,
return 0;
}
+static int hciops_read_local_oob_data(int index)
+{
+ int dd;
+ int err = 0;
+
+ dd = hci_open_dev(index);
+ if (dd < 0)
+ return -EIO;
+
+ err = hci_send_cmd(dd, OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA, 0, 0);
+ if (err < 0)
+ err = -errno;
+
+ hci_close_dev(dd);
+
+ return err;
+}
+
static struct btd_adapter_ops hci_ops = {
.setup = hciops_setup,
.cleanup = hciops_cleanup,
@@ -2335,6 +2380,7 @@ static struct btd_adapter_ops hci_ops = {
.write_le_host = hciops_write_le_host,
.get_remote_version = hciops_get_remote_version,
.encrypt_link = hciops_encrypt_link,
+ .read_local_oob_data = hciops_read_local_oob_data,
};
static int hciops_init(void)
diff --git a/src/adapter.c b/src/adapter.c
index b25a7fc..3ec562b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -3752,3 +3752,8 @@ int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
{
return adapter_ops->encrypt_link(adapter->dev_id, bdaddr, cb, user_data);
}
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
+{
+ return adapter_ops->read_local_oob_data(adapter->dev_id);
+}
diff --git a/src/adapter.h b/src/adapter.h
index aa4d686..4ca82b3 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -231,6 +231,7 @@ struct btd_adapter_ops {
gboolean delayed);
int (*encrypt_link) (int index, bdaddr_t *bdaddr, bt_hci_result_t cb,
gpointer user_data);
+ int (*read_local_oob_data) (int index);
};
int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
@@ -291,3 +292,5 @@ int btd_adapter_get_remote_version(struct btd_adapter *adapter,
int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
bt_hci_result_t cb, gpointer user_data);
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
diff --git a/src/device.c b/src/device.c
index b14865c..31cfd89 100644
--- a/src/device.c
+++ b/src/device.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2010 ST-Ericsson SA
*
*
* This program is free software; you can redistribute it and/or modify
@@ -59,6 +60,7 @@
#include "sdp-xml.h"
#include "storage.h"
#include "btio.h"
+#include "oob.h"
#define DEFAULT_XML_BUF_SIZE 1024
#define DISCONNECT_TIMER 2
@@ -132,6 +134,9 @@ struct btd_device {
uint8_t cap;
uint8_t auth;
+ uint8_t local_cap;
+ uint8_t local_auth;
+
uint16_t handle; /* Connection handle */
/* Whether were creating a security mode 3 connection */
@@ -149,6 +154,13 @@ struct btd_device {
gboolean has_debug_key;
uint8_t debug_key[16];
+
+ /* For OOB association model */
+ void (*oob_request_cb)(struct btd_device *device);
+ gboolean was_oob_ssp;
+ gboolean has_oob_data;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
};
static uint16_t uuid_list[] = {
@@ -830,6 +842,72 @@ static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
return NULL;
}
+void device_set_oob_data(struct btd_device *device, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ if (!device)
+ return;
+
+ if (hash && randomizer) {
+ memcpy(device->hash, hash, 16);
+ memcpy(device->randomizer, randomizer, 16);
+ device->has_oob_data = TRUE;
+ device->was_oob_ssp = TRUE;
+ }
+
+ if (device->oob_request_cb) {
+ device->oob_request_cb(device);
+ device->oob_request_cb = NULL;
+ }
+}
+
+gboolean device_get_oob_data(struct btd_device *device, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ if (!device || !device->has_oob_data)
+ return FALSE;
+
+ memcpy(hash, device->hash, 16);
+ memcpy(randomizer, device->randomizer, 16);
+ device->has_oob_data = FALSE;
+
+ return TRUE;
+}
+
+gboolean device_has_oob_data(struct btd_device *device)
+{
+ return device && device->has_oob_data;
+}
+
+gboolean device_request_oob_data(struct btd_device *device, void *cb)
+{
+ if (!device)
+ return FALSE;
+
+ device->was_oob_ssp = FALSE;
+ device->oob_request_cb = cb;
+ return oob_request_remote_data(device);
+}
+
+void device_set_local_auth_cap(struct btd_device *device, uint8_t auth,
+ uint8_t cap)
+{
+ if (!device)
+ return;
+
+ device->local_auth = auth;
+ device->local_cap = cap;
+}
+void device_get_local_auth_cap(struct btd_device *device, uint8_t *auth,
+ uint8_t *cap)
+{
+ if (!device)
+ return;
+
+ *auth = device->local_auth;
+ *cap = device->local_cap;
+}
+
static GDBusMethodTable device_methods[] = {
{ "GetProperties", "", "a{sv}", get_properties },
{ "SetProperty", "sv", "", set_property },
@@ -2282,7 +2360,7 @@ void device_cancel_authentication(struct btd_device *device, gboolean aborted)
gboolean device_is_authenticating(struct btd_device *device)
{
- return (device->authr != NULL);
+ return (device->authr != NULL || device->was_oob_ssp);
}
gboolean device_is_authorizing(struct btd_device *device)
diff --git a/src/device.h b/src/device.h
index b570bd1..b62cdc5 100644
--- a/src/device.h
+++ b/src/device.h
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2010 ST-Ericsson SA
*
*
* This program is free software; you can redistribute it and/or modify
@@ -89,6 +90,17 @@ void device_remove_connection(struct btd_device *device, DBusConnection *conn,
gboolean device_has_connection(struct btd_device *device, uint16_t handle);
void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
+void device_set_oob_data(struct btd_device *device, uint8_t *hash,
+ uint8_t *randomizer);
+gboolean device_get_oob_data(struct btd_device *device, uint8_t *hash,
+ uint8_t *randomizer);
+gboolean device_has_oob_data(struct btd_device *device);
+gboolean device_request_oob_data(struct btd_device *device, void *cb);
+void device_set_local_auth_cap(struct btd_device *device, uint8_t auth,
+ uint8_t cap);
+void device_get_local_auth_cap(struct btd_device *device, uint8_t *auth,
+ uint8_t *cap);
+
typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
void *user_data);
diff --git a/src/event.c b/src/event.c
index 60249f0..b551020 100644
--- a/src/event.c
+++ b/src/event.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2010 ST-Ericsson SA
*
*
* This program is free software; you can redistribute it and/or modify
@@ -757,26 +758,56 @@ void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer)
device_set_paired(device, TRUE);
}
-int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
- uint8_t *cap, uint8_t *auth)
+static void btd_event_io_cap_reply(struct btd_device *device)
+{
+ io_capability_reply_cp cp;
+ int dev;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ uint16_t dev_id = adapter_get_dev_id(adapter);
+
+ dev = hci_open_dev(dev_id);
+ if (dev < 0) {
+ error("hci_open_dev(%d): %s (%d)", dev_id,
+ strerror(errno), errno);
+ return;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ device_get_address(device, &cp.bdaddr);
+ device_get_local_auth_cap(device, &cp.authentication, &cp.capability);
+ cp.oob_data = device_has_oob_data(device)
+ ? OOB_DATA_PRESENT : OOB_DATA_NOT_PRESENT;
+
+ DBG("final capabilities reply is cap=0x%02x, auth=0x%02x, oob=0x%02x",
+ cp.capability, cp.authentication, cp.oob_data);
+
+ hci_send_cmd(dev, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
+ IO_CAPABILITY_REPLY_CP_SIZE, &cp);
+
+ hci_close_dev(dev);
+}
+
+int btd_event_request_io_cap(bdaddr_t *local, bdaddr_t *remote)
{
struct btd_adapter *adapter;
struct btd_device *device;
struct agent *agent = NULL;
uint8_t agent_cap;
int err;
+ uint8_t cap;
+ uint8_t auth;
if (!get_adapter_and_device(local, remote, &adapter, &device, TRUE))
return -ENODEV;
- err = btd_adapter_get_auth_info(adapter, remote, auth);
+ err = btd_adapter_get_auth_info(adapter, remote, &auth);
if (err < 0)
return err;
- DBG("initial authentication requirement is 0x%02x", *auth);
+ DBG("initial authentication requirement is 0x%02x", auth);
- if (*auth == 0xff)
- *auth = device_get_auth(device);
+ if (auth == 0xff)
+ auth = device_get_auth(device);
/* Check if the adapter is not pairable and if there isn't a bonding
* in progress */
@@ -785,11 +816,11 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
if (device_get_auth(device) < 0x02) {
DBG("Allowing no bonding in non-bondable mode");
/* No input, no output */
- *cap = 0x03;
+ cap = 0x03;
/* Kernel defaults to general bonding and so
* overwrite for this special case. Otherwise
* non-pairable test cases will fail. */
- *auth = 0x00;
+ auth = 0x00;
goto done;
}
return -EPERM;
@@ -805,13 +836,13 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
}
/* No agent available, and no bonding case */
- if (*auth == 0x00 || *auth == 0x04) {
+ if (auth == 0x00 || auth == 0x04) {
DBG("Allowing no bonding without agent");
/* No input, no output */
- *cap = 0x03;
+ cap = 0x03;
/* If kernel defaults to general bonding, set it
* back to no bonding */
- *auth = 0x00;
+ auth = 0x00;
goto done;
}
@@ -821,7 +852,7 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
agent_cap = agent_get_io_capability(agent);
- if (*auth == 0x00 || *auth == 0x04) {
+ if (auth == 0x00 || auth == 0x04) {
/* If remote requests dedicated bonding follow that lead */
if (device_get_auth(device) == 0x02 ||
device_get_auth(device) == 0x03) {
@@ -830,9 +861,9 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
* then require it, otherwise don't */
if (device_get_cap(device) == 0x03 ||
agent_cap == 0x03)
- *auth = 0x02;
+ auth = 0x02;
else
- *auth = 0x03;
+ auth = 0x03;
}
/* If remote indicates no bonding then follow that. This
@@ -840,7 +871,7 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
* as default. */
if (device_get_auth(device) == 0x00 ||
device_get_auth(device) == 0x01)
- *auth = 0x00;
+ auth = 0x00;
/* If remote requires MITM then also require it, unless
* our IO capability is NoInputNoOutput (so some
@@ -848,13 +879,19 @@ int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
if (device_get_auth(device) != 0xff &&
(device_get_auth(device) & 0x01) &&
agent_cap != 0x03)
- *auth |= 0x01;
+ auth |= 0x01;
}
- *cap = agent_get_io_capability(agent);
+ cap = agent_get_io_capability(agent);
done:
- DBG("final authentication requirement is 0x%02x", *auth);
+ DBG("final authentication requirement is 0x%02x", auth);
+
+ device_set_local_auth_cap(device, auth, cap);
+
+ /* If failed to request remote OOB data then reply immediately. */
+ if (!device_request_oob_data(device, btd_event_io_cap_reply))
+ btd_event_io_cap_reply(device);
return 0;
}
diff --git a/src/event.h b/src/event.h
index e918c9e..33d2f76 100644
--- a/src/event.h
+++ b/src/event.h
@@ -36,8 +36,7 @@ void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status);
void btd_event_write_simple_pairing_mode_complete(bdaddr_t *local);
void btd_event_read_simple_pairing_mode_complete(bdaddr_t *local, void *ptr);
void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
-int btd_event_get_io_cap(bdaddr_t *local, bdaddr_t *remote,
- uint8_t *cap, uint8_t *auth);
+int btd_event_request_io_cap(bdaddr_t *local, bdaddr_t *remote);
int btd_event_set_io_cap(bdaddr_t *local, bdaddr_t *remote,
uint8_t cap, uint8_t auth);
int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
diff --git a/src/oob.c b/src/oob.c
new file mode 100644
index 0000000..cc20c67
--- /dev/null
+++ b/src/oob.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <glib.h>
+#include "manager.h"
+#include "adapter.h"
+#include "oob.h"
+
+static struct oob_plugin *active_plugin = NULL;
+
+void oob_activate_plugin(struct oob_plugin *plugin)
+{
+ if (!plugin || !plugin->local_data_updated|| !plugin->plugin_deactivated
+ || !plugin->request_remote_data
+ || active_plugin == plugin)
+ return;
+
+ if (active_plugin)
+ active_plugin->plugin_deactivated();
+
+ active_plugin = plugin;
+}
+
+void oob_deactivate_plugin(struct oob_plugin *plugin)
+{
+ if (active_plugin == plugin)
+ active_plugin = NULL;
+}
+
+gboolean oob_request_remote_data(struct btd_device *device)
+{
+ return active_plugin && active_plugin->request_remote_data(device);
+}
+
+void oob_local_data_updated(bdaddr_t *ba, uint8_t *hash, uint8_t *randomizer)
+{
+ if (active_plugin)
+ active_plugin->local_data_updated(ba, hash, randomizer);
+}
diff --git a/src/oob.h b/src/oob.h
new file mode 100644
index 0000000..ed9fe84
--- /dev/null
+++ b/src/oob.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct oob_plugin
+{
+ /* If request was successfully send this functions should return TRUE.
+ * Function should not block for too long. */
+ gboolean (*request_remote_data)(struct btd_device *device);
+
+ /* Local OOB data updated. If corresponding HCI command failed, hash
+ * and randomizer are NULL */
+ void (*local_data_updated)(bdaddr_t *ba, uint8_t *hash,
+ uint8_t *randomizer);
+
+ /* Plug-in was deactivated (called only for active plug-in). */
+ void (*plugin_deactivated)(void);
+};
+
+/* These functions are called by OOB plug-in.*/
+void oob_activate_plugin(struct oob_plugin *plugin);
+void oob_deactivate_plugin(struct oob_plugin *plugin);
+
+/* These functions are called from stack to interact with OOB plug-in. */
+gboolean oob_request_remote_data(struct btd_device *device);
+void oob_local_data_updated(bdaddr_t *ba, uint8_t *hash, uint8_t *randomizer);
--
1.7.1
^ permalink raw reply related
* [PATCH 0/4] Support for out of band association model
From: Szymon Janc @ 2010-11-04 10:10 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Szymon Janc
This set of patches add support for out of band association model.
Suggestions from previous discussion ([RFC] D-Bus API for out of
band association model) are taken into account.
Also exemplary plugin which exports OOB functionality over DBus
(proposed API) is included.
Comments are welcome.
Szymon Janc (4):
Add support for Out of Band (OOB) association model.
Add DBus OOB plugin.
Add DBus OOB API documentation.
Add simple-oobprovider for testing.
Makefile.am | 11 ++-
acinclude.m4 | 6 +
doc/oob-api.txt | 62 ++++++++
lib/hci.h | 3 +
plugins/dbusoob.c | 356 +++++++++++++++++++++++++++++++++++++++++++++++
plugins/hciops.c | 70 ++++++++--
src/adapter.c | 5 +
src/adapter.h | 3 +
src/device.c | 80 +++++++++++-
src/device.h | 12 ++
src/event.c | 73 +++++++---
src/event.h | 3 +-
src/oob.c | 61 ++++++++
src/oob.h | 47 ++++++
test/simple-oobprovider | 54 +++++++
15 files changed, 811 insertions(+), 35 deletions(-)
create mode 100644 doc/oob-api.txt
create mode 100644 plugins/dbusoob.c
create mode 100644 src/oob.c
create mode 100644 src/oob.h
create mode 100755 test/simple-oobprovider
^ permalink raw reply
* [PATCH v2] Add support for generating pull response in many parts
From: Radoslaw Jablonski @ 2010-11-04 8:55 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Radoslaw Jablonski
In-Reply-To: <1288860932-9513-1-git-send-email-ext-jablonski.radoslaw@nokia.com>
Now data from tracker is fetched in many smaller parts (instead of one
big query before). This is needed to save memory and to not overload
dbus and tracker when generating query result.
---
plugins/phonebook-tracker.c | 71 ++++++++++++++++++++++++++++++++++++-------
1 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/plugins/phonebook-tracker.c b/plugins/phonebook-tracker.c
index 58f52ab..46ef5fb 100644
--- a/plugins/phonebook-tracker.c
+++ b/plugins/phonebook-tracker.c
@@ -57,6 +57,8 @@
#define COL_ANSWERED 37
#define ADDR_FIELD_AMOUNT 7
#define CONTACT_ID_PREFIX "contact:"
+#define QUERY_LIMIT_FORMAT "%s LIMIT %d OFFSET %d"
+#define QUERY_LIMIT 50
#define CONTACTS_QUERY_ALL \
"SELECT ?v nco:fullname(?c) " \
@@ -650,6 +652,9 @@ struct phonebook_data {
gboolean vcardentry;
const struct apparam_field *params;
GSList *contacts;
+ char *name;
+ int offset;
+ int num_row;
};
struct cache_data {
@@ -1098,6 +1103,12 @@ static void add_affiliation(char **field, const char *value)
*field = g_strdup(value);
}
+static char *gen_partial_query(const char *name, int limit, int offset)
+{
+ return g_strdup_printf(QUERY_LIMIT_FORMAT, name2query(name),
+ limit, offset);
+}
+
static void pull_contacts(char **reply, int num_fields, void *user_data)
{
struct phonebook_data *data = user_data;
@@ -1107,13 +1118,17 @@ static void pull_contacts(char **reply, int num_fields, void *user_data)
GString *vcards;
int last_index, i;
gboolean cdata_present = FALSE;
- char *home_addr, *work_addr;
+ gboolean last_resp = FALSE;
+ char *home_addr, *work_addr, *query;
if (num_fields < 0) {
data->cb(NULL, 0, num_fields, 0, data->user_data);
goto fail;
}
+ data->num_row++;
+ last_index = params->liststartoffset + params->maxlistcount;
+
DBG("reply %p", reply);
if (reply == NULL)
@@ -1145,8 +1160,6 @@ static void pull_contacts(char **reply, int num_fields, void *user_data)
data->index++;
- last_index = params->liststartoffset + params->maxlistcount;
-
if ((data->index <= params->liststartoffset ||
data->index > last_index) &&
params->maxlistcount > 0)
@@ -1222,14 +1235,46 @@ add_numbers:
done:
vcards = gen_vcards(data->contacts, params);
- if (num_fields == 0)
+ /* If tracker returned only empty row - all results already returned */
+ if (num_fields == 0 && data->num_row == 1)
+ last_resp = TRUE;
+
+ /* Check if tracker could return desired number of results - if coldn't,
+ * all results are fetched already and this is last response */
+ if (data->num_row < QUERY_LIMIT)
+ last_resp = TRUE;
+
+ /* Check needed for 'maxlistcount' and 'liststartoffset' parameters */
+ if (data->index > last_index)
+ last_resp = TRUE;
+
+ /* Data won't be send if starting offset has not been achieved (unless
+ * now handling last response from tracker) */
+ if (data->index > params->liststartoffset || last_resp)
+ /* 4th parameter of callback is used to mark if stream should be
+ * closed or more data will be sent*/
data->cb(vcards->str, vcards->len,
- g_slist_length(data->contacts), 0,
- data->user_data);
+ g_slist_length(data->contacts), !last_resp,
+ data->user_data);
+ g_slist_free(data->contacts);data->contacts = NULL;
g_string_free(vcards, TRUE);
+ data->num_row = 0;
+
+ /* Sending query to tracker to get next part of results (only for pull
+ * phonebook queries) */
+ if (!data->vcardentry && !last_resp) {
+ data->offset += QUERY_LIMIT;
+ query = gen_partial_query(data->name, QUERY_LIMIT,
+ data->offset);
+ query_tracker(query, PULL_QUERY_COL_AMOUNT,
+ pull_contacts, data);
+ g_free(query);
+ return;
+ }
fail:
g_slist_free(data->contacts);
+ g_free(data->name);
g_free(data);
}
@@ -1367,18 +1412,18 @@ int phonebook_pull(const char *name, const struct apparam_field *params,
phonebook_cb cb, void *user_data)
{
struct phonebook_data *data;
- const char *query;
+ char *query;
reply_list_foreach_t pull_cb;
- int col_amount;
+ int col_amount, ret;
DBG("name %s", name);
if (params->maxlistcount == 0) {
- query = name2count_query(name);
+ query = g_strdup(name2count_query(name));
col_amount = COUNT_QUERY_COL_AMOUNT;
pull_cb = pull_contacts_size;
} else {
- query = name2query(name);
+ query = gen_partial_query(name, QUERY_LIMIT, 0);
col_amount = PULL_QUERY_COL_AMOUNT;
pull_cb = pull_contacts;
}
@@ -1390,8 +1435,12 @@ int phonebook_pull(const char *name, const struct apparam_field *params,
data->params = params;
data->user_data = user_data;
data->cb = cb;
+ data->name = g_strdup(name);
+
+ ret = query_tracker(query, col_amount, pull_cb, data);
+ g_free(query);
- return query_tracker(query, col_amount, pull_cb, data);
+ return ret;
}
int phonebook_get_entry(const char *folder, const char *id,
--
1.7.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox