The following patches were cretated off of the working bluez tip at git.kernel.org as of:
commit f0411254277e6c2273d32379e4a2d3742ee05e55
Author: Johan Hedberg <[email protected]>
Date: Wed Jan 19 22:20:41 2011 +0530
Update NREC callback code to originally intended v2 state
Patch 1 adds the default MTU for L2CAP based ATT connections (48) and
modifies the usages for consistency. This is important when segmenting
long attribute over multiple transactions, because remote clients will
typically decide wether to issue subsequent READ_BLOB request based on wether
the prior read filled the entire MTU. This explicitely does not address
situations where a larger MTU than the defaults (23 and 48) are used, but
in practice, there is no evidence that they exist at this time.
Patch 2 is some maintanence to fix the attrib-server to correctly indicate
the end of the service range, if a primary or secondary service contains
nothing but the service declaration. This can be seen prior to this patch
in attrib-server.c in the GATT_PRIM_SVC_UUID definition which sent a service
range of 0x0010 - 0x0006 instead of 0x0010 - 0x0010.
Patch 3 completes the implementation. It can be tested on the current example
server by reading attribute 0x0214, which caontains a 25 octet string.
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Brian,
On Wed, Jan 19, 2011, Brian Gix wrote:
> ---
> attrib/att.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
> attrib/att.h | 6 +++++-
> src/attrib-server.c | 39 +++++++++++++++++++++++++++++++++++++--
> 3 files changed, 86 insertions(+), 3 deletions(-)
This one has also been pushed upstream. Thanks.
Johan
Hi Brian,
On Wed, Jan 19, 2011, Brian Gix wrote:
> ---
> src/attrib-server.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/src/attrib-server.c b/src/attrib-server.c
> index 04bc1ec..f38d61a 100644
> --- a/src/attrib-server.c
> +++ b/src/attrib-server.c
> @@ -242,6 +242,7 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
>
> last_size = a->len;
> old = cur;
> + last_handle = cur->handle;
> }
>
> if (groups == NULL)
Pushed upstream. Thanks.
Johan
Hi Brian,
On Wed, Jan 19, 2011, Brian Gix wrote:
> + if (io == le_io)
> + channel->mtu = ATT_DEFAULT_LE_MTU;
> + else
> + channel->mtu = ATT_DEFAULT_L2CAP_MTU;
> +
I don't think that will work. le_io is the server socket and io is the
new client socket. So they will never be one and the same. Did you
actually test this code?
Johan
Hi All -- Correction below.
On Wed, 2011-01-19 at 14:00 -0800, Brian Gix wrote:
> The following patches were cretated off of the working bluez tip at git.kernel.org as of:
>
> commit f0411254277e6c2273d32379e4a2d3742ee05e55
> Author: Johan Hedberg <[email protected]>
> Date: Wed Jan 19 22:20:41 2011 +0530
>
> Update NREC callback code to originally intended v2 state
>
>
> Patch 1 adds the default MTU for L2CAP based ATT connections (48) and
> modifies the usages for consistency. This is important when segmenting
> long attribute over multiple transactions, because remote clients will
> typically decide wether to issue subsequent READ_BLOB request based on wether
> the prior read filled the entire MTU. This explicitely does not address
> situations where a larger MTU than the defaults (23 and 48) are used, but
> in practice, there is no evidence that they exist at this time.
>
> Patch 2 is some maintanence to fix the attrib-server to correctly indicate
> the end of the service range, if a primary or secondary service contains
> nothing but the service declaration. This can be seen prior to this patch
> in attrib-server.c in the GATT_PRIM_SVC_UUID definition which sent a service
> range of 0x0010 - 0x0006 instead of 0x0010 - 0x0010.
What I meant was it is the GENERIC_ATTRIB_PROFILE_ID service which is
(prior to this patch) indicating an incorrect and invalid range.
> Patch 3 completes the implementation. It can be tested on the current example
> server by reading attribute 0x0214, which caontains a 25 octet string.
>
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
---
attrib/att.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
attrib/att.h | 6 +++++-
src/attrib-server.c | 39 +++++++++++++++++++++++++++++++++++++--
3 files changed, 86 insertions(+), 3 deletions(-)
diff --git a/attrib/att.c b/attrib/att.c
index f8dbc02..dff8597 100644
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -582,6 +582,33 @@ uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle)
return min_len;
}
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint16_t *offset)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+ sizeof(*offset);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (handle == NULL)
+ return 0;
+
+ if (offset == NULL)
+ return 0;
+
+ if (len < min_len)
+ return 0;
+
+ if (pdu[0] != ATT_OP_READ_BLOB_REQ)
+ return 0;
+
+ *handle = att_get_u16(&pdu[1]);
+ *offset = att_get_u16(&pdu[3]);
+
+ return min_len;
+}
+
uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
{
if (pdu == NULL)
@@ -600,6 +627,23 @@ uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
return vlen + 1;
}
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+ uint8_t *pdu, int len)
+{
+ if (pdu == NULL)
+ return 0;
+
+ vlen -= offset;
+ if (vlen > len - 1)
+ vlen = len - 1;
+
+ pdu[0] = ATT_OP_READ_BLOB_RESP;
+
+ memcpy(pdu + 1, &value[offset], vlen);
+
+ return vlen + 1;
+}
+
uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen)
{
if (pdu == NULL)
diff --git a/attrib/att.h b/attrib/att.h
index a4f6ab1..8803ae3 100644
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -217,9 +217,13 @@ 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 enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
- int len);
+ int len);
uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle);
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+ uint16_t *offset);
uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+ 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);
diff --git a/src/attrib-server.c b/src/attrib-server.c
index f38d61a..631b43e 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -546,6 +546,33 @@ static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
return enc_read_resp(a->data, a->len, pdu, len);
}
+static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
+ uint16_t offset, uint8_t *pdu, int len)
+{
+ struct attribute *a;
+ uint8_t status;
+ GSList *l;
+ guint h = handle;
+
+ l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ if (!l)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+ ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+ a = l->data;
+
+ if (a->len <= offset)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+ ATT_ECODE_INVALID_OFFSET, pdu, len);
+
+ status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_reqs);
+ if (status)
+ return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status,
+ pdu, len);
+
+ return enc_read_blob_resp(a->data, a->len, offset, pdu, len);
+}
+
static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
const uint8_t *value, int vlen,
uint8_t *pdu, int len)
@@ -599,7 +626,7 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
{
struct gatt_channel *channel = user_data;
uint8_t opdu[ATT_MAX_MTU], value[ATT_MAX_MTU];
- uint16_t length, start, end, mtu;
+ uint16_t length, start, end, mtu, offset;
uuid_t uuid;
uint8_t status = 0;
int vlen;
@@ -634,6 +661,15 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
length = read_value(channel, start, opdu, channel->mtu);
break;
+ case ATT_OP_READ_BLOB_REQ:
+ length = dec_read_blob_req(ipdu, len, &start, &offset);
+ if (length == 0) {
+ status = ATT_ECODE_INVALID_PDU;
+ goto done;
+ }
+
+ length = read_blob(channel, start, offset, opdu, channel->mtu);
+ break;
case ATT_OP_MTU_REQ:
length = dec_mtu_req(ipdu, len, &mtu);
if (length == 0) {
@@ -679,7 +715,6 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
length = find_by_type(start, end, &uuid, value, vlen,
opdu, channel->mtu);
break;
- case ATT_OP_READ_BLOB_REQ:
case ATT_OP_READ_MULTI_REQ:
case ATT_OP_PREP_WRITE_REQ:
case ATT_OP_EXEC_WRITE_REQ:
--
1.7.1
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
---
src/attrib-server.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 04bc1ec..f38d61a 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -242,6 +242,7 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
last_size = a->len;
old = cur;
+ last_handle = cur->handle;
}
if (groups == NULL)
--
1.7.1
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
LE and BR/EDR (standard L2CAP) have different default MTU sizes.
The LE deafult is 23, and the L2CAP default is 48. The LE default is
properly used for all GATT Commands, as they have all been designed
to fit within that constraint. However the attribute server should
use the actual MTU of the transport being used.
---
attrib/att.h | 3 ++-
attrib/gatt.c | 28 ++++++++++++++--------------
src/attrib-server.c | 6 +++++-
3 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/attrib/att.h b/attrib/att.h
index a1e0b62..a4f6ab1 100644
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -107,7 +107,8 @@
#define ATT_MAX_MTU 256
-#define ATT_DEFAULT_MTU 23
+#define ATT_DEFAULT_LE_MTU 23
+#define ATT_DEFAULT_L2CAP_MTU 48
/* Requirements for read/write operations */
enum {
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 5d7887e..f3b513e 100644
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -105,7 +105,7 @@ static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
struct discover_primary *dp = user_data;
GSList *ranges, *last;
struct att_range *range;
- uint8_t opdu[ATT_DEFAULT_MTU];
+ uint8_t opdu[ATT_DEFAULT_LE_MTU];
guint16 oplen;
int err = 0;
@@ -195,7 +195,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
err = 0;
if (end != 0xffff) {
- uint8_t opdu[ATT_DEFAULT_MTU];
+ uint8_t opdu[ATT_DEFAULT_LE_MTU];
guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
opdu, sizeof(opdu));
@@ -214,7 +214,7 @@ guint gatt_discover_primary(GAttrib *attrib, uuid_t *uuid, gatt_cb_t func,
gpointer user_data)
{
struct discover_primary *dp;
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
GAttribResultFunc cb;
guint16 plen;
@@ -245,7 +245,7 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
struct discover_char *dc = user_data;
struct att_data_list *list;
unsigned int i, err;
- uint8_t opdu[ATT_DEFAULT_MTU];
+ uint8_t opdu[ATT_DEFAULT_LE_MTU];
guint16 oplen;
uuid_t uuid;
uint16_t last = 0;
@@ -314,7 +314,7 @@ done:
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
gatt_cb_t func, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
struct discover_char *dc;
guint16 plen;
uuid_t uuid;
@@ -342,7 +342,7 @@ guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
uuid_t *uuid, GAttribResultFunc func,
gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
plen = enc_read_by_type_req(start, end, uuid, pdu, sizeof(pdu));
@@ -381,7 +381,7 @@ static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
gpointer user_data)
{
struct read_long_data *long_read = user_data;
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint8 *tmp;
guint16 plen;
guint id;
@@ -402,7 +402,7 @@ static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
long_read->buffer = tmp;
long_read->size += rlen - 1;
- if (rlen < ATT_DEFAULT_MTU)
+ if (rlen < ATT_DEFAULT_LE_MTU)
goto done;
plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
@@ -427,11 +427,11 @@ static void read_char_helper(guint8 status, const guint8 *rpdu,
guint16 rlen, gpointer user_data)
{
struct read_long_data *long_read = user_data;
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
guint id;
- if (status != 0 || rlen < ATT_DEFAULT_MTU)
+ if (status != 0 || rlen < ATT_DEFAULT_LE_MTU)
goto done;
long_read->buffer = g_malloc(rlen);
@@ -461,7 +461,7 @@ done:
guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
guint id;
struct read_long_data *long_read;
@@ -493,7 +493,7 @@ guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
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];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
plen = enc_write_req(handle, value, vlen, pdu, sizeof(pdu));
@@ -504,7 +504,7 @@ guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
GAttribResultFunc func, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_MTU];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
plen = enc_find_info_req(start, end, pdu, sizeof(pdu));
@@ -518,7 +518,7 @@ guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
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];
+ uint8_t pdu[ATT_DEFAULT_LE_MTU];
guint16 plen;
plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 7e85d17..04bc1ec 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -722,8 +722,12 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data)
return;
}
+ if (io == le_io)
+ channel->mtu = ATT_DEFAULT_LE_MTU;
+ else
+ channel->mtu = ATT_DEFAULT_L2CAP_MTU;
+
channel->attrib = g_attrib_new(io);
- channel->mtu = ATT_DEFAULT_MTU;
g_io_channel_unref(io);
channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_EVENTS,
--
1.7.1
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum