Add SDP registration of Primary Services
cover Add SDP registration of Primary Services
This is a fully merged patch to take into account Claudio's changes to Device Name Characteristic etcetra.
Otherwise:
This addresses Anderson's issue with the SDP registration. It maintains the prior APIs and adds a couple which can be called any time after a complete Primary Service has been inserted into the attribute database.
This allows the attrib-server user to pick and choose the services he wants to expose to BR/EDR devices, as opposed to LE only devices. It also gives a mechanism to remove the SDP record from the SDP db.
It also has improved naming (attrib_*) to tie all the parts together.
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
PATCH 1/1 Add SDP registration of Primary GATT services
Add capability to register SDP record for Primary Services.
--- attrib/example.c | 29 +++++++++- src/attrib-server.c | 162 +++++++++++++++++++++++++++++++++++++++------------ src/attrib-server.h | 3 +- 3 files changed, 155 insertions(+), 39 deletions(-)
attrib/example.c +28 -1
@@ -59,6 +59,9 @@#define FMT_KILOGRAM_UUID 0xA010#define FMT_HANGING_UUID 0xA011 +#define SDP_RECORD_COUNT 10 +uint32_t sdp_handles[SDP_RECORD_COUNT ]; +static int register_attributes (void ) {const char *desc_out_temp ="Outside Temperature" ;
@@ -77,6 +80,7 @@ static int register_attributes(void)uint8_t atval[256 ];uuid_t uuid;int len; +int i =0 ;/* Battery state service: primary service definition */ sdp_uuid16_create (&uuid,GATT_PRIM_SVC_UUID );
@@ -101,6 +105,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 */ + sdp_handles[i] =attrib_create_sdp (0x0100 ,"Battery State Service" ); +if (sdp_handles[i]) + i++; +/* Thermometer: primary service definition */ sdp_uuid16_create (&uuid,GATT_PRIM_SVC_UUID );att_put_u16 (THERM_HUMIDITY_SVC_UUID , &atval[0 ]);
@@ -116,7 +125,8 @@ static int register_attributes(void)/* Thermometer: Include */ att_put_u16 (0x0550 , &atval[0 ]);att_put_u16 (0x0568 , &atval[2 ]); -attrib_db_add (0x0202 , &uuid,ATT_NONE ,ATT_NOT_PERMITTED , atval,4 ); +att_put_u16 (VENDOR_SPECIFIC_SVC_UUID , &atval[4 ]); +attrib_db_add (0x0202 , &uuid,ATT_NONE ,ATT_NOT_PERMITTED , atval,6 );/* Thermometer: temperature characteristic */ sdp_uuid16_create (&uuid,GATT_CHARAC_UUID );
@@ -173,6 +183,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 */ + sdp_handles[i] =attrib_create_sdp (0x0200 ,"Thermometer" ); +if (sdp_handles[i]) + i++; +/* Secondary Service: Manufacturer Service */ sdp_uuid16_create (&uuid,GATT_SND_SVC_UUID );att_put_u16 (MANUFACTURER_SVC_UUID , &atval[0 ]);
@@ -299,6 +314,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 */ + sdp_handles[i] =attrib_create_sdp (0x0680 ,"Weight Service" ); +if (sdp_handles[i]) + i++; +return 0 ; }
@@ -309,4 +329,11 @@ int server_example_init(void)void server_example_exit (void ) { +int i; + +for (i =0 ; i <SDP_RECORD_COUNT ; i++) +if (sdp_handles[i]) { +attrib_free_sdp (sdp_handles[i]); + sdp_handles[i] =0 ; + } }
src/attrib-server.c +125 -37
240 lines not on this page · raw message
src/attrib-server.h +2 -1
@@ -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);
Signature · 6 lines
1.7.1 -- Brian Gix [email protected] Employee of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Re: [PATCH 1/1 rev2] Add SDP registration of Primary GATT services
Hi Brian,
Quoted 12 lines · Brian Gix
Add capability to register SDP record for Primary Services.
---
attrib/example.c | 29 +++++++++-
src/attrib-server.c | 162 +++++++++++++++++++++++++++++++++++++++------------src/attrib-server.h| 3 +-3 files changed, 155 insertions(+), 39 deletions(-)attrib/example.c +0
-0
attrib/example.c line 64
Quoted 6 lines
attrib/example.c +3
-0@@ -59,6 +59,9 @@ #define FMT_KILOGRAM_UUID 0xA010 #define FMT_HANGING_UUID 0xA011 +#define SDP_RECORD_COUNT 10 +uint32_t sdp_handles[SDP_RECORD_COUNT ]; +
This should be static, right?
What about using a GSList *, instead of a statically allocated structure?
Quoted 24 lines
attrib/example.c +6
-0static int register_attributes(void) { const char *desc_out_temp = "Outside Temperature";@@ -77,6 +80,7 @@ static int register_attributes(void) uint8_t atval[256]; uuid_t uuid; int len; + int i = 0; /* Battery state service: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);@@ -101,6 +105,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 */ + sdp_handles[i] = attrib_create_sdp(0x0100, "Battery State Service"); + if (sdp_handles[i]) + i++; + /* Thermometer: primary service definition */ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
attrib/example.c static int register_attributes(void) · line 129
Quoted 6 lines
attrib/example.c +2
-1@@ -116,7 +125,8 @@ static int register_attributes(void) /* Thermometer: Include */ att_put_u16(0x0550, &atval[0]); att_put_u16(0x0568, &atval[2]); - attrib_db_add(0x0202, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4); + att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]); + attrib_db_add(0x0202, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
This change does not seem realted to the patch.
Quoted 28 lines
attrib/example.c +10
-0/* Thermometer: temperature characteristic */ sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);@@ -173,6 +183,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 */ + sdp_handles[i] = attrib_create_sdp(0x0200, "Thermometer"); + if (sdp_handles[i]) + i++; + /* Secondary Service: Manufacturer Service */ sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID); att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);@@ -299,6 +314,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 */ + sdp_handles[i] = attrib_create_sdp(0x0680, "Weight Service"); + if (sdp_handles[i]) + i++; + return 0; }
attrib/example.c int server_example_init(void) · line 338
Quoted 10 lines
attrib/example.c +7
-0@@ -309,4 +329,11 @@ int server_example_init(void) void server_example_exit(void) { + int i; + + for (i = 0; i < SDP_RECORD_COUNT; i++) + if (sdp_handles[i]) { + attrib_free_sdp(sdp_handles[i]); + sdp_handles[i] = 0; + }
Again, using GSList will make this simpler.
Quoted 71 lines
attrib/example.c +0
-0}src/attrib-server.c +2
-1@@ -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};src/attrib-server.c +1
-1-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_troot_uuid,proto_uuid,gatt_uuid, l2cap;
-sdp_profile_desc_tprofile;
sdp_record_t *record;
sdp_data_t *psm, *sh, *eh;
-uint16_tlp =GATT_PSM, start =0x0001, end =0xffff;
+uint16_tlp =GATT_PSM;src/attrib-server.c +6
-0+ + if (uuid == NULL) + return NULL; + + if (start > end) + return NULL;record =
sdp_record_alloc();
if (record == NULL)src/attrib-server.c +1
-13@@ -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);
src/attrib-server.c static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode, · line 179
Quoted 6 lines
src/attrib-server.c +3
-0@@ -180,6 +174,40 @@ static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode, return 0; } +static struct attribute *find_primary_range (uint16_t start,uint16_t *end) +{ + struct attribute *a, *attrib = NULL;
Move declaration of "struct attribute *a" to the for(), because it is used only there.
src/attrib-server.c static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode, · line 190
Quoted 11 lines
src/attrib-server.c +11
-0+ GSList *l; + gboolean found = FALSE; + + if (end == NULL) + return NULL; + + for (l = database; l; l = l->next) { + a = l->data; + + if (a->handle < start) + continue;
I find the logic of this loop too complex.
What about using g_slist_find_custom() (like in other places of GATT code) to find the attribute with the given handle, them check it is a primary service definition? You can then iterate directly from the next attribute, e.g.:
guint h = handle;
l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp); if (!l)
return NULL;
attrib = found_prim->data;
*end = start; // a sane default
for (l = found_prim->next; l; l = l->next) {
Then, you could the check for primary/secondary service to know if the service has ended (just pasting your own code):
src/attrib-server.c static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode, · line 203
Quoted 3 lines
src/attrib-server.c +3
-0+if (sdp_uuid_cmp (&a->uuid, &prim_uuid) ==0 || +sdp_uuid_cmp (&a->uuid, &snd_uuid) ==0 ) +break ;
You can then update the *end:
*end = a->handle;
} // end of for()
return attrib;
At this point *end should contain the corrent *end. Not tested :) Next, you could check if the iteration was until the end of the list:
Quoted 24 lines
src/attrib-server.c +20
-0+ + if ((a->handle == start) && + sdp_uuid_cmp(&a->uuid, &prim_uuid) == 0) { + found = TRUE; + *end = start; + attrib = a; + continue; + } else if (!found) + break; + + if (sdp_uuid_cmp(&a->uuid, &prim_uuid) == 0 || + sdp_uuid_cmp(&a->uuid, &snd_uuid) == 0) + break; + else + *end = a->handle; + } + + return attrib; +} +static
uint16_tread_by_group(struct gatt_channel *channel, uint16_t start, uint16_t end, uuid_t *uuid, uint8_t *pdu, int len)
src/attrib-server.c static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, · line 645
Quoted 8 lines
src/attrib-server.c +1
-1@@ -611,7 +639,7 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu, uint8_t *pdu, int len) { - channel->mtu = MIN(mtu, ATT_MAX_MTU); + channel->mtu = MIN(mtu, channel->mtu); return enc_mtu_resp(channel->mtu, pdu, len); }
I still think this change should go in a separate patch, as it fixes another bug.
Quoted 89 lines
src/attrib-server.c +35
-18@@ -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"); + if (!register_core_services()) goto failed; - } - - if (add_record_to_server(BDADDR_ANY, record) < 0) { - error("Failed to register GATT service record"); - sdp_record_free(record); - goto failed; - } - - sdp_handle = record->handle; - - register_core_services(); if (!main_opts.le) return 0;@@ -934,8 +969,61 @@ 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;
src/attrib-server.c void attrib_server_exit(void) · line 993
Quoted 10 lines
src/attrib-server.c +10
-0+ uuid_t svc; + + a = find_primary_range(handle, &end); + + if (a == NULL) + goto failed; + + if (a->len == 2) { + svc.type = SDP_UUID16; + svc.value.uuid16 = att_get_u16(a->data);
You should use "sdp_uuid16_create(&svc, att_get_u16(a->data))" here
src/attrib-server.c void attrib_server_exit(void) · line 996
Quoted 3 lines
src/attrib-server.c +3
-0+ } else if (a->len == 16) { + svc.type = SDP_UUID128; + memcpy(&svc.value.uuid128, a->data, sizeof(uint128_t));
And sdp_uuid128_create() here.
src/attrib-server.c void attrib_server_exit(void) · line 1009
Quoted 13 lines
src/attrib-server.c +13
-0+ } else + goto failed; + + record = server_record_new(&svc, handle, end); + + if (record == NULL) + goto failed; + + if (name) + sdp_set_info_attr(record, name, "BlueZ", NULL); + + if (svc.type == SDP_UUID16 && + svc.value.uuid16 == GENERIC_ACCESS_PROFILE_ID) {
Usually you would create a temporary uuid16 containing GENERIC_ACCESS_PROFILE_ID and then compare with sdp_uuid_cmp().
src/attrib-server.c void attrib_server_exit(void) · line 1020
Quoted 10 lines
src/attrib-server.c +10
-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; +failed:
This label (and the goto's) look unnecessary.
Quoted 22 lines
src/attrib-server.c +6
-0+ 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,src/attrib-server.h +2
-2@@ -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); --
Quoted 7 lines
1.7.1
--
Brian Gix
[email protected]
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Regards,
Signature · 3 lines
Anderson Lizardo Instituto Nokia de Tecnologia - INdT Manaus - Brazil