This adds sending exchange MTU request on connection. This is send only
if no previous negotiaion was done (as client or server).
This is needed to pass TC_GAC_CL_BV_01_C.
---
android/gatt.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 88 insertions(+), 2 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 89da60d..55ffed5 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -144,6 +144,7 @@ struct gatt_device {
gatt_device_state_t state;
+ size_t negotiated_mtu;
GAttrib *attrib;
GIOChannel *att_io;
struct queue *services;
@@ -979,6 +980,86 @@ static void send_app_connect_notifications(void *data, void *user_data)
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_device *device = user_data;
+ GError *gerr = NULL;
+ uint16_t cid, imtu, rmtu, mtu;
+ GIOChannel *io;
+
+ if (status) {
+ error("gatt: MTU exchange: %s", att_ecode2str(status));
+ goto failed;
+ }
+
+ if (!dec_mtu_resp(pdu, plen, &rmtu)) {
+ error("gatt: MTU exchange: protocol error");
+ goto failed;
+ }
+
+ if (rmtu < ATT_DEFAULT_LE_MTU) {
+ error("gatt: MTU exchange: mtu error");
+ goto failed;
+ }
+
+ io = g_attrib_get_channel(device->attrib);
+
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Failed to acquire imtu and cid: %s",
+ gerr->message);
+ g_error_free(gerr);
+
+ goto failed;
+ }
+
+ if (cid != ATT_CID)
+ goto failed;
+
+ mtu = MIN(rmtu, imtu);
+ if (mtu != imtu && !g_attrib_set_mtu(device->attrib, rmtu)) {
+ error("gatt: MTU exchange failed");
+ goto failed;
+ }
+
+ device->negotiated_mtu = mtu;
+ DBG("MTU exchange succeeded: rmtu:%d, imtu:%d, set mtu: %d", rmtu,
+ imtu, mtu);
+
+failed:
+ device_unref(device);
+}
+
+static void send_exchange_mtu_request(struct gatt_device *device)
+{
+ GError *gerr = NULL;
+ uint16_t cid, imtu;
+ GIOChannel *io;
+
+ io = g_attrib_get_channel(device->attrib);
+
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: Failed to acquire imtu and cid: %s",
+ gerr->message);
+ g_error_free(gerr);
+
+ return;
+ }
+
+ if (cid != ATT_CID)
+ return;
+
+ if (!gatt_exchange_mtu(device->attrib, imtu, exchange_mtu_cb,
+ device_ref(device)))
+ device_unref(device);
+}
+
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct gatt_device *dev = user_data;
@@ -1012,6 +1093,7 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
goto reply;
}
+ device->negotiated_mtu = 0;
dev->attrib = attrib;
dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
disconnected_cb, dev);
@@ -1026,6 +1108,10 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
status = GATT_SUCCESS;
+ /* If mtu not negotiated yet - exchange mtu */
+ if (!dev->negotiated_mtu)
+ send_exchange_mtu_request(dev);
+
reply:
data.dev = dev;
data.status = status;
@@ -4513,8 +4599,8 @@ static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
}
/* Limit OMTU to received value */
- mtu = MIN(mtu, omtu);
- g_attrib_set_mtu(dev->attrib, mtu);
+ dev->negotiated_mtu = MIN(mtu, omtu);
+ g_attrib_set_mtu(dev->attrib, dev->negotiated_mtu);
/* Respond with our IMTU */
len = enc_mtu_resp(imtu, rsp, rsp_size);
--
1.9.3
This replaces fixed size pdu usage with g_attrib buffer when possible.
When only received packets are decoded we use dynamic allocation with
current mtu.
---
android/gatt.c | 123 +++++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 98 insertions(+), 25 deletions(-)
diff --git a/android/gatt.c b/android/gatt.c
index 55ffed5..7f89c9d 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -3582,7 +3582,8 @@ static bool is_service(const bt_uuid_t *type)
static void send_dev_pending_response(struct gatt_device *device,
uint8_t opcode)
{
- uint8_t rsp[ATT_DEFAULT_LE_MTU];
+ size_t mtu;
+ uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
struct pending_request *val;
uint16_t len = 0;
uint8_t error = 0;
@@ -3627,7 +3628,7 @@ static void send_dev_pending_response(struct gatt_device *device,
val = queue_pop_head(temp);
}
- len = enc_read_by_type_resp(adl, rsp, sizeof(rsp));
+ len = enc_read_by_type_resp(adl, rsp, mtu);
att_data_list_free(adl);
queue_destroy(temp, destroy_pending_request);
@@ -3642,7 +3643,7 @@ static void send_dev_pending_response(struct gatt_device *device,
}
len = enc_read_blob_resp(val->value, val->length, val->offset,
- rsp, sizeof(rsp));
+ rsp, mtu);
destroy_pending_request(val);
break;
case ATT_OP_READ_REQ:
@@ -3652,7 +3653,7 @@ static void send_dev_pending_response(struct gatt_device *device,
goto done;
}
- len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+ len = enc_read_resp(val->value, val->length, rsp, mtu);
destroy_pending_request(val);
break;
case ATT_OP_READ_BY_GROUP_REQ: {
@@ -3698,7 +3699,7 @@ static void send_dev_pending_response(struct gatt_device *device,
val = queue_pop_head(temp);
}
- len = enc_read_by_grp_resp(adl, rsp, sizeof(rsp));
+ len = enc_read_by_grp_resp(adl, rsp, mtu);
att_data_list_free(adl);
queue_destroy(temp, destroy_pending_request);
@@ -3746,7 +3747,7 @@ static void send_dev_pending_response(struct gatt_device *device,
}
if (list && !error)
- len = enc_find_by_type_resp(list, rsp, sizeof(rsp));
+ len = enc_find_by_type_resp(list, rsp, mtu);
else
error = ATT_ECODE_ATTR_NOT_FOUND;
@@ -3782,7 +3783,7 @@ static void send_dev_pending_response(struct gatt_device *device,
}
len = enc_prep_write_resp(val->handle, val->offset, val->value,
- val->length, rsp, sizeof(rsp));
+ val->length, rsp, mtu);
destroy_pending_request(val);
break;
default:
@@ -3791,8 +3792,7 @@ static void send_dev_pending_response(struct gatt_device *device,
done:
if (!len)
- len = enc_error_resp(opcode, 0x0000, error, rsp,
- ATT_DEFAULT_LE_MTU);
+ len = enc_error_resp(opcode, 0x0000, error, rsp, mtu);
g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
@@ -4228,10 +4228,11 @@ failed:
static void handle_server_send_indication(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_send_indication *cmd = buf;
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
struct app_connection *conn;
uint8_t status;
uint16_t length;
+ uint8_t *pdu;
+ size_t mtu;
DBG("");
@@ -4242,15 +4243,17 @@ static void handle_server_send_indication(const void *buf, uint16_t len)
goto reply;
}
+ pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
+
if (cmd->confirm)
/* TODO: Add data to track confirmation for this request */
length = enc_indication(cmd->attribute_handle,
- (uint8_t *)cmd->value, cmd->len,
- pdu, sizeof(pdu));
+ (uint8_t *)cmd->value, cmd->len, pdu,
+ mtu);
else
length = enc_notification(cmd->attribute_handle,
(uint8_t *)cmd->value, cmd->len,
- pdu, sizeof(pdu));
+ pdu, mtu);
g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, NULL);
@@ -4679,10 +4682,22 @@ static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
return 0;
}
+static size_t get_device_att_mtu(struct gatt_device *device)
+{
+ size_t mtu;
+
+ if (device->negotiated_mtu)
+ return device->negotiated_mtu;
+
+ g_attrib_get_buffer(device->attrib, &mtu);
+
+ return mtu;
+}
+
static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *device)
{
- uint8_t search_value[ATT_DEFAULT_LE_MTU];
+ uint8_t *search_value;
size_t search_vlen;
uint16_t start, end;
uint16_t handle;
@@ -4692,14 +4707,24 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
DBG("");
+ search_value = malloc0(get_device_att_mtu(device));
+ if (!search_value)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid,
search_value, &search_vlen);
- if (!len)
+ if (!len) {
+ free(search_value);
+
return ATT_ECODE_INVALID_PDU;
+ }
q = queue_new();
- if (!q)
+ if (!q) {
+ free(search_value);
+
return ATT_ECODE_UNLIKELY;
+ }
gatt_db_find_by_type(gatt_db, start, end, &uuid, q);
@@ -4710,6 +4735,8 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
data = new0(struct pending_request, 1);
if (!data) {
queue_destroy(q, NULL);
+ free(search_value);
+
return ATT_ECODE_INSUFF_RESOURCES;
}
@@ -4717,6 +4744,8 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
if (!data) {
destroy_pending_request(data);
queue_destroy(q, NULL);
+ free(search_value);
+
return ATT_ECODE_INSUFF_RESOURCES;
}
@@ -4731,6 +4760,7 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
}
queue_destroy(q, NULL);
+ free(search_value);
process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
@@ -4740,14 +4770,30 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t *value;
uint16_t handle;
uint16_t len;
size_t vlen;
+ value = malloc0(get_device_att_mtu(dev));
+ if (!value)
+ return;
+
len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
- if (!len)
+ if (!len) {
+ free(value);
+
return;
+ }
+
+ if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
+ &dev->bdaddr)) {
+ free(value);
+
+ return;
+ }
+
+ free(value);
gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], &dev->bdaddr);
}
@@ -4755,18 +4801,30 @@ static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t *value;
uint16_t handle;
uint16_t len;
size_t vlen;
+ value = malloc0(get_device_att_mtu(dev));
+ if (!value)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
- if (!len)
+ if (!len) {
+ free(value);
+
return ATT_ECODE_INVALID_PDU;
+ }
if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
- &dev->bdaddr))
+ &dev->bdaddr)) {
+ free(value);
+
return ATT_ECODE_UNLIKELY;
+ }
+
+ free(value);
return 0;
}
@@ -4774,20 +4832,32 @@ static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint8_t value[ATT_DEFAULT_LE_MTU];
+ uint8_t *value;
uint16_t handle;
uint16_t offset;
uint16_t len;
size_t vlen;
+ value = malloc0(get_device_att_mtu(dev));
+ if (!value)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
value, &vlen);
- if (!len)
+ if (!len) {
+ free(value);
+
return ATT_ECODE_INVALID_PDU;
+ }
if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0],
- &dev->bdaddr))
+ &dev->bdaddr)) {
+ free(value);
+
return ATT_ECODE_UNLIKELY;
+ }
+
+ free(value);
return 0;
}
@@ -5156,9 +5226,10 @@ static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
bdaddr_t *bdaddr,
void *user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
struct gatt_device *dev;
uint16_t length;
+ size_t mtu;
+ uint8_t *pdu;
dev = find_device_by_addr(bdaddr);
if (!dev) {
@@ -5166,6 +5237,8 @@ static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
return;
}
+ pdu = g_attrib_get_buffer(dev->attrib, &mtu);
+
/* TODO handle CCC */
/* Set services changed notification flag */
--
1.9.3