Return-Path: From: Brian Gix To: linux-bluetooth@vger.kernel.org Cc: johan.hedberg@nokia.com, padovan@profusion.mobi, anderson.lizardo@openbossa.org, Brian Gix Subject: [PATCH 1/2] Add SDP registration of Primary GATT services Date: Fri, 18 Feb 2011 15:37:40 -0800 Message-Id: <1298072261-7232-2-git-send-email-bgix@codeaurora.org> In-Reply-To: <1298072261-7232-1-git-send-email-bgix@codeaurora.org> References: <1298072261-7232-1-git-send-email-bgix@codeaurora.org> Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Add capability to register SDP record for Primary Services. --- attrib/example.c | 24 ++++++++ src/attrib-server.c | 157 +++++++++++++++++++++++++++++++++++++++------------ src/attrib-server.h | 3 +- 3 files changed, 147 insertions(+), 37 deletions(-) diff --git a/attrib/example.c b/attrib/example.c index 1911912..cd18168 100644 --- a/attrib/example.c +++ b/attrib/example.c @@ -59,6 +59,8 @@ #define FMT_KILOGRAM_UUID 0xA010 #define FMT_HANGING_UUID 0xA011 +static GSList *sdp_handles = NULL; + static int register_attributes(void) { const char *desc_out_temp = "Outside Temperature"; @@ -75,6 +77,7 @@ static int register_attributes(void) 0xD4, 0x49, 0x11, 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE }; uint8_t atval[256]; + uint32_t handle; uuid_t uuid; int len; @@ -101,6 +104,11 @@ static int register_attributes(void) atval[1] = 0x00; attrib_db_add(0x0111, &uuid, ATT_NONE, ATT_AUTHENTICATION, atval, 2); + /* Add an SDP record for the above service */ + handle = attrib_create_sdp(0x0100, "Battery State Service"); + if (handle) + sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle)); + /* Thermometer: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]); @@ -173,6 +181,11 @@ static int register_attributes(void) strncpy((char *) atval, desc_out_hum, len); attrib_db_add(0x0214, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); + /* Add an SDP record for the above service */ + handle = attrib_create_sdp(0x0200, "Thermometer"); + if (handle) + sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle)); + /* Secondary Service: Manufacturer Service */ sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]); @@ -299,6 +312,11 @@ static int register_attributes(void) strncpy((char *) atval, desc_weight, len); attrib_db_add(0x0685, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len); + /* Add an SDP record for the above service */ + handle = attrib_create_sdp(0x0680, "Weight Service"); + if (handle) + sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle)); + return 0; } @@ -309,4 +327,10 @@ int server_example_init(void) void server_example_exit(void) { + while (sdp_handles) { + uint32_t handle = GPOINTER_TO_UINT(sdp_handles->data); + + attrib_free_sdp(handle); + sdp_handles = g_slist_remove(sdp_handles, sdp_handles->data); + } } diff --git a/src/attrib-server.c b/src/attrib-server.c index 5e00601..6966ad0 100644 --- a/src/attrib-server.c +++ b/src/attrib-server.c @@ -70,7 +70,8 @@ struct group_elem { static GIOChannel *l2cap_io = NULL; static GIOChannel *le_io = NULL; static GSList *clients = NULL; -static uint32_t sdp_handle = 0; +static uint32_t gatt_sdp_handle = 0; +static uint32_t gap_sdp_handle = 0; /* GAP attribute handles */ static uint16_t name_handle = 0x0000; @@ -85,14 +86,19 @@ static uuid_t snd_uuid = { .value.uuid16 = GATT_SND_SVC_UUID }; -static sdp_record_t *server_record_new(void) +static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end) { - sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto; + sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; uuid_t root_uuid, proto_uuid, gatt_uuid, l2cap; - sdp_profile_desc_t profile; sdp_record_t *record; sdp_data_t *psm, *sh, *eh; - uint16_t lp = GATT_PSM, start = 0x0001, end = 0xffff; + uint16_t lp = GATT_PSM; + + if (uuid == NULL) + return NULL; + + if (start > end) + return NULL; record = sdp_record_alloc(); if (record == NULL) @@ -103,17 +109,10 @@ static sdp_record_t *server_record_new(void) sdp_set_browse_groups(record, root); sdp_list_free(root, NULL); - sdp_uuid16_create(&gatt_uuid, GENERIC_ATTRIB_SVCLASS_ID); - svclass_id = sdp_list_append(NULL, &gatt_uuid); + svclass_id = sdp_list_append(NULL, uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); - sdp_uuid16_create(&profile.uuid, GENERIC_ATTRIB_PROFILE_ID); - profile.version = 0x0100; - profiles = sdp_list_append(NULL, &profile); - sdp_set_profile_descs(record, profiles); - sdp_list_free(profiles, NULL); - sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); @@ -131,11 +130,6 @@ static sdp_record_t *server_record_new(void) aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); - sdp_set_info_attr(record, "Generic Attribute Profile", "BlueZ", NULL); - - sdp_set_url_attr(record, "http://www.bluez.org/", - "http://www.bluez.org/", "http://www.bluez.org/"); - sdp_set_service_id(record, gatt_uuid); sdp_data_free(psm); @@ -530,6 +524,40 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2) return attrib1->handle - attrib2->handle; } +static struct attribute *find_primary_range(uint16_t start, uint16_t *end) +{ + struct attribute *attrib; + guint h = start; + GSList *l; + + if (end == NULL) + return NULL; + + l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp); + + if (!l) + return NULL; + + attrib = l->data; + + if (sdp_uuid_cmp(&attrib->uuid, &prim_uuid) != 0) + return NULL; + + *end = start; + + for (l = l->next; l; l = l->next) { + struct attribute *a = l->data; + + if (sdp_uuid_cmp(&a->uuid, &prim_uuid) == 0 || + sdp_uuid_cmp(&a->uuid, &snd_uuid) == 0) + break; + + *end = a->handle; + } + + return attrib; +} + static uint16_t read_value(struct gatt_channel *channel, uint16_t handle, uint8_t *pdu, int len) { @@ -798,7 +826,7 @@ static void confirm_event(GIOChannel *io, void *user_data) return; } -static void register_core_services(void) +static gboolean register_core_services(void) { uint8_t atval[256]; uuid_t uuid; @@ -835,17 +863,37 @@ static void register_core_services(void) att_put_u16(appearance, &atval[0]); attrib_db_add(appearance_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); + gap_sdp_handle = attrib_create_sdp(0x0001, "Generic Access Profile"); + + if (gap_sdp_handle == 0) { + error("Failed to register GAP service record"); + goto failed; + } /* GATT service: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]); attrib_db_add(0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); + + gatt_sdp_handle = attrib_create_sdp(0x0010, + "Generic Attribute Profile"); + if (gatt_sdp_handle == 0) { + error("Failed to register GATT service record"); + goto failed; + } + + return TRUE; + +failed: + if (gap_sdp_handle) + remove_record_from_server(gap_sdp_handle); + + return FALSE; } int attrib_server_init(void) { GError *gerr = NULL; - sdp_record_t *record; /* BR/EDR socket */ l2cap_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event, @@ -861,21 +909,8 @@ int attrib_server_init(void) return -1; } - record = server_record_new(); - if (record == NULL) { - error("Unable to create GATT service record"); - goto failed; - } - - if (add_record_to_server(BDADDR_ANY, record) < 0) { - error("Failed to register GATT service record"); - sdp_record_free(record); + if (!register_core_services()) goto failed; - } - - sdp_handle = record->handle; - - register_core_services(); if (!main_opts.le) return 0; @@ -934,8 +969,58 @@ void attrib_server_exit(void) g_slist_free(clients); - if (sdp_handle) - remove_record_from_server(sdp_handle); + if (gatt_sdp_handle) + remove_record_from_server(gatt_sdp_handle); + + if (gap_sdp_handle) + remove_record_from_server(gap_sdp_handle); +} + +uint32_t attrib_create_sdp(uint16_t handle, const char *name) +{ + sdp_record_t *record; + struct attribute *a; + uint16_t end = 0; + uuid_t svc, gap_uuid; + + a = find_primary_range(handle, &end); + + if (a == NULL) + return 0; + + if (a->len == 2) + sdp_uuid16_create(&svc, att_get_u16(a->data)); + else if (a->len == 16) + sdp_uuid128_create(&svc, a->data); + else + return 0; + + record = server_record_new(&svc, handle, end); + + if (record == NULL) + return 0; + + if (name) + sdp_set_info_attr(record, name, "BlueZ", NULL); + + sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID); + 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 (add_record_to_server(BDADDR_ANY, record) < 0) + sdp_record_free(record); + else + return record->handle; + + return 0; +} + +void attrib_free_sdp(uint32_t sdp_handle) +{ + remove_record_from_server(sdp_handle); } int attrib_db_add(uint16_t handle, uuid_t *uuid, int read_reqs, int write_reqs, diff --git a/src/attrib-server.h b/src/attrib-server.h index 252700f..ecd695c 100644 --- a/src/attrib-server.h +++ b/src/attrib-server.h @@ -30,5 +30,6 @@ int attrib_db_add(uint16_t handle, uuid_t *uuid, int read_reqs, int write_reqs, int attrib_db_update(uint16_t handle, uuid_t *uuid, const uint8_t *value, int len); int attrib_db_del(uint16_t handle); - int attrib_gap_set(uint16_t uuid, const uint8_t *value, int len); +uint32_t attrib_create_sdp(uint16_t handle, const char *name); +void attrib_free_sdp(uint32_t sdp_handle); -- 1.7.1 -- Brian Gix bgix@codeaurora.org Employee of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum