From 41ca7b23c306a1e50fb4f124ef7d584884dae7e6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 18 Feb 2015 16:46:35 +0200 Subject: [PATCH] core/gatt-database: Add support for BR/EDR This adds support for BR/EDR in addition to LE. --- src/gatt-database.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/gatt-database.c b/src/gatt-database.c index 3f3ab3db2..370b85270 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -25,6 +25,8 @@ #include #include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" #include "lib/uuid.h" #include "btio/btio.h" #include "src/shared/util.h" @@ -41,6 +43,10 @@ #define ATT_CID 4 #endif +#ifndef ATT_PSM +#define ATT_PSM 31 +#endif + #define UUID_GAP 0x1800 #define UUID_GATT 0x1801 @@ -49,6 +55,9 @@ struct btd_gatt_database { struct gatt_db *db; unsigned int db_id; GIOChannel *le_io; + GIOChannel *l2cap_io; + uint32_t gap_handle; + uint32_t gatt_handle; struct queue *device_states; struct gatt_db_attribute *svc_chngd; struct gatt_db_attribute *svc_chngd_ccc; @@ -195,6 +204,18 @@ static void gatt_database_free(void *data) 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->gatt_handle) + adapter_service_remove(database->adapter, + database->gatt_handle); + + if (database->gap_handle) + adapter_service_remove(database->adapter, database->gap_handle); + /* TODO: Persistently store CCC states before freeing them */ queue_destroy(database->device_states, device_state_free); gatt_db_unregister(database->db, database->db_id); @@ -297,6 +318,94 @@ done: gatt_db_attribute_read_result(attrib, id, error, value, len); } +static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end) +{ + sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; + uuid_t root_uuid, proto_uuid, l2cap; + sdp_record_t *record; + sdp_data_t *psm, *sh, *eh; + uint16_t lp = ATT_PSM; + + if (uuid == NULL) + return NULL; + + if (start > end) + return NULL; + + record = sdp_record_alloc(); + if (record == NULL) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + sdp_list_free(root, NULL); + + svclass_id = sdp_list_append(NULL, uuid); + sdp_set_service_classes(record, svclass_id); + sdp_list_free(svclass_id, NULL); + + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&proto_uuid, ATT_UUID); + proto[1] = sdp_list_append(NULL, &proto_uuid); + sh = sdp_data_alloc(SDP_UINT16, &start); + proto[1] = sdp_list_append(proto[1], sh); + eh = sdp_data_alloc(SDP_UINT16, &end); + proto[1] = sdp_list_append(proto[1], eh); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sdp_data_free(psm); + sdp_data_free(sh); + sdp_data_free(eh); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto, NULL); + + return record; +} + +static uint32_t database_add_record(struct btd_gatt_database *database, + uint16_t uuid, + struct gatt_db_attribute *attr, + const char *name) +{ + sdp_record_t *record; + uint16_t start, end; + uuid_t svc, gap_uuid; + + sdp_uuid16_create(&svc, uuid); + gatt_db_attribute_get_service_handles(attr, &start, &end); + + record = record_new(&svc, start, end); + if (!record) + return 0; + + if (name != NULL) + sdp_set_info_attr(record, name, "BlueZ", NULL); + + sdp_uuid16_create(&gap_uuid, UUID_GAP); + if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) { + sdp_set_url_attr(record, "http://www.bluez.org/", + "http://www.bluez.org/", + "http://www.bluez.org/"); + } + + if (adapter_service_add(database->adapter, record) == 0) + return record->handle; + + sdp_record_free(record); + return 0; +} + static void populate_gap_service(struct btd_gatt_database *database) { bt_uuid_t uuid; @@ -305,6 +414,8 @@ static void populate_gap_service(struct btd_gatt_database *database) /* Add the GAP service */ bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(database->db, &uuid, true, 5); + database->gap_handle = database_add_record(database, UUID_GAP, service, + "Generic Access Profile"); /* * Device Name characteristic. @@ -471,6 +582,9 @@ static void populate_gatt_service(struct btd_gatt_database *database) /* Add the GATT service */ bt_uuid16_create(&uuid, UUID_GATT); service = gatt_db_add_service(database->db, &uuid, true, 4); + database->gatt_handle = database_add_record(database, UUID_GATT, + service, + "Generic Attribute Profile"); gatt_db_attribute_get_service_handles(service, &start_handle, NULL); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); @@ -673,6 +787,18 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) goto fail; } + /* BR/EDR socket */ + database->l2cap_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_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (database->l2cap_io == NULL) { + error("Failed to start listening: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + register_core_services(database); return database; -- 2.47.3