#include "lib/uuid.h"
#include "gdbus/gdbus.h"
+#include "btio/btio.h"
#include "log.h"
#include "error.h"
+#include "hcid.h"
#include "adapter.h"
#include "device.h"
#include "src/shared/io.h"
#define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
+#define EATT_MAX_BEARERS 2
+
struct btd_gatt_client {
struct btd_device *device;
+ uint8_t features;
bool ready;
char devaddr[18];
struct gatt_db *db;
notify_client_free(notify_client);
}
+static void eatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct btd_gatt_client *client = user_data;
+
+ if (gerr)
+ return;
+
+ device_attach_att(client->device, io);
+}
+
+static void eatt_connect(struct btd_gatt_client *client)
+{
+ struct btd_device *dev = client->device;
+ struct btd_adapter *adapter = device_get_adapter(dev);
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+ int i;
+
+ ba2str(device_get_address(dev), addr);
+
+ DBG("Connection attempt to: %s", addr);
+
+ for (i = 0; i < EATT_MAX_BEARERS; i++) {
+ /* Fallback to regular LE mode */
+ io = bt_io_connect(eatt_connect_cb, client, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(dev),
+ BT_IO_OPT_DEST_TYPE,
+ device_get_le_address_type(dev),
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("EATT bt_io_connect(%s): %s", addr,
+ gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ g_io_channel_unref(io);
+ }
+}
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
static void check_chrcs_ready(void *data, void *user_data)
{
DBG("GATT client ready");
create_services(client);
+
+ DBG("Features 0x%02x", client->features);
+
+ if (!client->features) {
+ client->features = bt_gatt_client_get_features(client->gatt);
+ DBG("Update Features 0x%02x", client->features);
+ if (client->features & BT_GATT_CHRC_CLI_FEAT_EATT)
+ eatt_connect(client);
+ }
+
#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
/*
* In case of more number of services and services having
* for any pre-registered notification sessions.
*/
queue_foreach(client->all_notify_clients, register_notify, client);
+
+ if (!(client->features & BT_GATT_CHRC_CLI_FEAT_EATT))
+ return;
+
+ eatt_connect(client);
}
void btd_gatt_client_service_added(struct btd_gatt_client *client,
#include "profile.h"
#include "service.h"
-#ifndef ATT_CID
-#define ATT_CID 4
-#endif
-
-#ifndef ATT_PSM
-#define ATT_PSM 31
-#endif
-
#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
#define GATT_PROFILE_IFACE "org.bluez.GattProfile1"
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
struct gatt_db *db;
unsigned int db_id;
GIOChannel *le_io;
- GIOChannel *l2cap_io;
+ GIOChannel *eatt_io;
+ GIOChannel *bredr_io;
struct queue *records;
struct queue *device_states;
struct queue *ccc_callbacks;
struct gatt_db_attribute *svc_chngd_ccc;
struct gatt_db_attribute *cli_feat;
struct gatt_db_attribute *db_hash;
+ struct gatt_db_attribute *eatt;
struct queue *apps;
struct queue *profiles;
};
g_io_channel_unref(database->le_io);
}
- if (database->l2cap_io) {
- g_io_channel_shutdown(database->l2cap_io, FALSE, NULL);
- g_io_channel_unref(database->l2cap_io);
+ if (database->eatt_io) {
+ g_io_channel_shutdown(database->eatt_io, FALSE, NULL);
+ g_io_channel_unref(database->eatt_io);
+ }
+
+ if (database->bredr_io) {
+ g_io_channel_shutdown(database->bredr_io, FALSE, NULL);
+ g_io_channel_unref(database->bredr_io);
}
/* TODO: Persistently store CCC states before freeing them */
uuid_t root_uuid, proto_uuid, l2cap;
sdp_record_t *record;
sdp_data_t *psm, *sh, *eh;
- uint16_t lp = ATT_PSM;
+ uint16_t lp = BT_ATT_PSM;
if (uuid == NULL)
return NULL;
{
struct btd_gatt_database *database = user_data;
struct device_state *state;
+ uint8_t bits[] = { BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING,
+ BT_GATT_CHRC_CLI_FEAT_EATT };
uint8_t ecode = 0;
+ unsigned int i;
DBG("Client Features write");
goto done;
}
- /* A client shall never clear a bit it has set.
- * TODO: make it generic to any bits.
- */
- if (state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING &&
- !(value[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) {
- ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED;
- goto done;
+ for (i = 0; i < sizeof(bits); i++) {
+ /* A client shall never clear a bit it has set */
+ if (state->cli_feat[0] & (1 << i) && !(value[0] & (1 << i))) {
+ ecode = BT_ATT_ERROR_VALUE_NOT_ALLOWED;
+ goto done;
+ }
}
/* Shall we reallocate the feat array if bigger? */
len--;
}
- state->cli_feat[0] &= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING;
+ state->cli_feat[0] &= ((1 << sizeof(bits)) - 1);
state->change_aware = true;
done:
state->change_aware = true;
}
+static void server_feat_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ struct device_state *state;
+ uint8_t ecode = 0;
+ uint8_t value = 0;
+
+ state = get_device_state(database, att);
+ if (!state) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ value |= BT_GATT_CHRC_SERVER_FEAT_EATT;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value));
+}
+
static void populate_gatt_service(struct btd_gatt_database *database)
{
bt_uuid_t uuid;
/* Add the GATT service */
bt_uuid16_create(&uuid, UUID_GATT);
- service = gatt_db_add_service(database->db, &uuid, true, 8);
+ service = gatt_db_add_service(database->db, &uuid, true, 10);
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
&uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
db_hash_read_cb, NULL, database);
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVER_FEAT);
+ database->eatt = gatt_db_service_add_characteristic(service,
+ &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ server_feat_read_cb, NULL, database);
+
gatt_db_service_set_active(service, true);
database_add_record(database, service);
BT_IO_OPT_SOURCE_BDADDR, addr,
BT_IO_OPT_SOURCE_TYPE,
btd_adapter_get_address_type(adapter),
- BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_CID, BT_ATT_CID,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
if (!database->le_io) {
goto fail;
}
+ /* EATT socket */
+ database->eatt_io = bt_io_listen(connect_cb, NULL, NULL, NULL,
+ &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_SOURCE_TYPE,
+ btd_adapter_get_address_type(adapter),
+ BT_IO_OPT_PSM, BT_ATT_EATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_MTU, main_opts.gatt_mtu,
+ BT_IO_OPT_INVALID);
+ if (!database->eatt_io) {
+ g_error_free(gerr);
+ goto fail;
+ }
+
/* BR/EDR socket */
- database->l2cap_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+ database->bredr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, addr,
- BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_PSM, BT_ATT_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_MTU, main_opts.gatt_mtu,
BT_IO_OPT_INVALID);
- if (database->l2cap_io == NULL) {
+ if (database->bredr_io == NULL) {
error("Failed to start listening: %s", gerr->message);
g_error_free(gerr);
goto fail;