It can be useful for programs using GATT to be aware of the maximum transmittable attribute size, as negotiated by bluetoothd. This change exposes the negotiated size over D-Bus, if such a negotiation has occurred. --- attrib/gattrib.c | 6 +++++- doc/device-api.txt | 11 +++++++++++ src/device.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/shared/att.c | 15 +++++++++++++++ src/shared/gatt-server.c | 1 + src/shared/gatt-server.h | 3 +++ 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/attrib/gattrib.c b/attrib/gattrib.c index 2e1e39a..f080b6b 100644 --- a/attrib/gattrib.c +++ b/attrib/gattrib.c @@ -468,7 +468,11 @@ gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu) attrib->buflen = mtu; - return bt_att_set_mtu(attrib->att, mtu); + if (bt_att_set_mtu(attrib->att, mtu)) { + bt_att_set_mtu_negotiated(attrib->att, true); + return true; + } else + return false; } gboolean g_attrib_unregister(GAttrib *attrib, guint id) diff --git a/doc/device-api.txt b/doc/device-api.txt index 13b2881..fc04011 100644 --- a/doc/device-api.txt +++ b/doc/device-api.txt @@ -234,3 +234,14 @@ Properties string Address [readonly] array{byte} AdvertisingFlags [readonly, experimental] The Advertising Data Flags of the remote device. + + int16 NegotiatedMaxAttributeSize [readonly, + optional, + experimental] + + The maximum supported byte size of attributes, as + negotiated between this host and the remote device. + This attribute is only available once the negotiation + is complete. If the attribute is not available, + clients should assume the default maximum size for + the underlying technology, e.g. 20 bytes for BTLE. diff --git a/src/device.c b/src/device.c index 8693eb8..cd3983b 100644 --- a/src/device.c +++ b/src/device.c @@ -92,6 +92,8 @@ #define GATT_INCLUDE_UUID_STR "2802" #define GATT_CHARAC_UUID_STR "2803" +#define ATT_PACKET_HEADER_SIZE 3 + static DBusConnection *dbus_conn = NULL; static unsigned service_state_cb_id; @@ -930,6 +932,41 @@ static gboolean dev_property_exists_tx_power(const GDBusPropertyTable *property, return TRUE; } +static gboolean dev_property_get_negotiated_max_att_payload( + const GDBusPropertyTable * property, + DBusMessageIter *iter, void *data) +{ + const struct btd_device *dev = data; + dbus_int16_t val = 0; + int err; + + /* The spec for this API requires a failure if + * MTU negotiation is incomplete. + */ + if (!bt_att_is_mtu_negotiated(dev->att)) + return FALSE; + + val = bt_att_get_mtu(dev->att); + // subtract off the ATT header length to get the max payload + val -= ATT_PACKET_HEADER_SIZE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val); + + return TRUE; +} + +static gboolean dev_property_exists_negotiated_max_att_payload( + const GDBusPropertyTable * property, + void *data) +{ + const struct btd_device *dev = data; + + /* The spec for this API requires the property + * be absent until MTU negotiation is incomplete. + */ + return bt_att_is_mtu_negotiated(dev->att); +} + static gboolean dev_property_get_svc_resolved(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) @@ -2561,6 +2598,10 @@ static const GDBusPropertyTable device_properties[] = { NULL, dev_property_service_data_exist }, { "TxPower", "n", dev_property_get_tx_power, NULL, dev_property_exists_tx_power }, + { "NegotiatedMaxAttributeSize", "n", + dev_property_get_negotiated_max_att_payload, + NULL, dev_property_exists_negotiated_max_att_payload, + G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL }, { "AdvertisingFlags", "ay", dev_property_get_flags, NULL, dev_property_flags_exist, diff --git a/src/shared/att.c b/src/shared/att.c index ca2d051..bf20a01 100644 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -70,6 +70,7 @@ struct bt_att { uint8_t *buf; uint16_t mtu; + bool mtu_negotiated; unsigned int next_send_id; /* IDs for "send" ops */ unsigned int next_reg_id; /* IDs for registered callbacks */ @@ -1100,6 +1101,20 @@ bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback, return true; } +bool bt_att_is_mtu_negotiated(struct bt_att *att) +{ + if (!att) + return false; + + return att->mtu_negotiated; +} + +void bt_att_set_mtu_negotiated(struct bt_att *att, bool negotiated) +{ + if (att && !att->mtu_negotiated) + att->mtu_negotiated = negotiated; +} + uint16_t bt_att_get_mtu(struct bt_att *att) { if (!att) diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 79e01c8..c76ca23 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -1379,6 +1379,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, /* Set MTU to be the minimum */ server->mtu = final_mtu; bt_att_set_mtu(server->att, final_mtu); + bt_att_set_mtu_negotiated(server->att, true); util_debug(server->debug_callback, server->debug_data, "MTU exchange complete, with MTU: %u", final_mtu); diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h index 0e480e1..0b99da5 100644 --- a/src/shared/gatt-server.h +++ b/src/shared/gatt-server.h @@ -31,6 +31,9 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db, struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server); void bt_gatt_server_unref(struct bt_gatt_server *server); +int bt_gatt_server_get_negotiated_mtu(const struct bt_gatt_server *server, + int16_t *mtu); + typedef void (*bt_gatt_server_destroy_func_t)(void *user_data); typedef void (*bt_gatt_server_debug_func_t)(const char *str, void *user_data); typedef void (*bt_gatt_server_conf_func_t)(void *user_data); -- 2.10.0 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html