diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf135d38..e253573b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added CC256x: With ENABLE_CC256X_ASSISTED_HFP, HFP enables WBS codec on demand and configures PCM/I2S interface for 8kH/16kHz +SDP Client RFCOMM: add `sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid` ### Fixed L2CAP: fix packet size check for incoming classic basic channels (regression introduced in v1.2.1) diff --git a/src/classic/sdp_client_rfcomm.c b/src/classic/sdp_client_rfcomm.c index 2cb42e82b..3e9a6388b 100644 --- a/src/classic/sdp_client_rfcomm.c +++ b/src/classic/sdp_client_rfcomm.c @@ -53,41 +53,63 @@ #include "classic/sdp_util.h" #include "hci_cmd.h" +#if (SDP_SERVICE_NAME_LEN < 16) +#error "SDP_SERVICE_NAME_LEN must be at least 16 bytes" +#endif + // called by test/sdp_client void sdp_client_query_rfcomm_init(void); -typedef enum { +static enum { GET_PROTOCOL_LIST_LENGTH = 1, GET_PROTOCOL_LENGTH, GET_PROTOCOL_ID_HEADER_LENGTH, GET_PROTOCOL_ID, GET_PROTOCOL_VALUE_LENGTH, GET_PROTOCOL_VALUE -} pdl_state_t; +} protokoll_descriptor_list_state; +static enum { + GET_SERVICE_LIST_LENGTH = 1, + GET_SERVICE_LIST_ITEM_GET_UUID_TYPE, + GET_SERVICE_LIST_ITEM, + GET_SERVICE_LIST_ITEM_SHORT, + GET_SERVICE_LIST_ITEM_LONG, + GET_SERVICE_INVALID, +} service_class_id_list_state; // higher layer query - get rfcomm channel and name // All attributes: 0x0001 - 0x0100 static const uint8_t des_attributeIDList[] = { 0x35, 0x05, 0x0A, 0x00, 0x01, 0x01, 0x00}; -static uint8_t sdp_service_name[SDP_SERVICE_NAME_LEN+1]; -static uint8_t sdp_service_name_len = 0; -static uint8_t sdp_rfcomm_channel_nr = 0; -static uint8_t sdp_service_name_header_size; +static uint8_t sdp_service_name[SDP_SERVICE_NAME_LEN+1]; +static uint8_t sdp_service_name_len = 0; +static uint8_t sdp_rfcomm_channel_nr = 0; + +static uint8_t sdp_service_name_header_size; + +static bool sdp_client_rfcomm_serviceclass_matched; +static bool sdp_client_rfcomm_match_serviceclass; +static uint16_t sdp_client_rfcomm_uuid16; + +static int protocol_value_bytes_received = 0; +static int protocol_value_size; +static int protocol_offset; +static int protocol_size; +static int protocol_id_bytes_to_read; +static uint32_t protocol_id = 0; -static pdl_state_t pdl_state = GET_PROTOCOL_LIST_LENGTH; -static int protocol_value_bytes_received = 0; -static uint16_t protocol_id = 0; -static int protocol_offset; -static int protocol_size; -static int protocol_id_bytes_to_read; -static int protocol_value_size; static de_state_t de_header_state; -static de_state_t sn_de_header_state; static btstack_packet_handler_t sdp_app_callback; // +static void sdp_rfcomm_query_prepare(void){ + sdp_rfcomm_channel_nr = 0; + sdp_service_name[0] = 0; + sdp_client_rfcomm_serviceclass_matched = false; +} + static void sdp_rfcomm_query_emit_service(void){ uint8_t event[3+SDP_SERVICE_NAME_LEN+1]; event[0] = SDP_EVENT_QUERY_RFCOMM_SERVICE; @@ -95,8 +117,81 @@ static void sdp_rfcomm_query_emit_service(void){ event[2] = sdp_rfcomm_channel_nr; (void)memcpy(&event[3], sdp_service_name, sdp_service_name_len); event[3+sdp_service_name_len] = 0; - (*sdp_app_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); - sdp_rfcomm_channel_nr = 0; + (*sdp_app_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event)); +} + +static void sdp_client_query_rfcomm_handle_record_parsed(void){ + if (sdp_rfcomm_channel_nr == 0) return; + if (sdp_client_rfcomm_match_serviceclass && (sdp_client_rfcomm_serviceclass_matched == false)) return; + sdp_rfcomm_query_emit_service(); + sdp_rfcomm_query_prepare(); +} + +// Format: DE Sequence of UUIDs +static void sdp_client_query_rfcomm_handle_service_class_list_data(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){ + UNUSED(attribute_value_length); + + // init state on first byte + if (data_offset == 0){ + service_class_id_list_state = GET_SERVICE_LIST_LENGTH; + de_state_init(&de_header_state); + } + + // process data + switch(service_class_id_list_state){ + + case GET_SERVICE_LIST_LENGTH: + // read DES sequence header + if (!de_state_size(data, &de_header_state)) break; + service_class_id_list_state = GET_SERVICE_LIST_ITEM_GET_UUID_TYPE; + break; + + case GET_SERVICE_LIST_ITEM_GET_UUID_TYPE: + protocol_id = 0; + protocol_offset = 0; + // validate UUID type + if (de_get_element_type(&data) != DE_UUID) { + service_class_id_list_state = GET_SERVICE_INVALID; + break; + } + // get UUID length + protocol_id_bytes_to_read = de_get_data_size(&data); + if (protocol_id_bytes_to_read > 16) { + service_class_id_list_state = GET_SERVICE_INVALID; + break; + } + service_class_id_list_state = GET_SERVICE_LIST_ITEM; + break; + + case GET_SERVICE_LIST_ITEM: + sdp_service_name[protocol_offset++] = data; + protocol_id_bytes_to_read--; + if (protocol_id_bytes_to_read > 0) break; + // parse 2/4/16 bytes UUID + switch (protocol_offset){ + case 2: + protocol_id = big_endian_read_16(sdp_service_name, 0); + break; + case 4: + protocol_id = big_endian_read_32(sdp_service_name, 0); + break; + case 16: + if (uuid_has_bluetooth_prefix(sdp_service_name)){ + protocol_id = big_endian_read_32(sdp_service_name, 0); + } + break; + default: + break; + } + if (protocol_id == sdp_client_rfcomm_uuid16){ + sdp_client_rfcomm_serviceclass_matched = true; + } + service_class_id_list_state = GET_SERVICE_LIST_ITEM_GET_UUID_TYPE; + break; + + default: + break; + } } static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_t attribute_value_length, uint32_t data_offset, uint8_t data){ @@ -104,16 +199,16 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ // init state on first byte if (data_offset == 0){ - pdl_state = GET_PROTOCOL_LIST_LENGTH; + de_state_init(&de_header_state); + protokoll_descriptor_list_state = GET_PROTOCOL_LIST_LENGTH; } - - switch(pdl_state){ + switch(protokoll_descriptor_list_state){ case GET_PROTOCOL_LIST_LENGTH: if (!de_state_size(data, &de_header_state)) break; - pdl_state = GET_PROTOCOL_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH; break; case GET_PROTOCOL_LENGTH: @@ -124,7 +219,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ protocol_offset = de_header_state.de_offset; protocol_size = de_header_state.de_size; - pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_ID_HEADER_LENGTH; break; case GET_PROTOCOL_ID_HEADER_LENGTH: @@ -133,7 +228,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ protocol_id = 0; protocol_id_bytes_to_read = de_header_state.de_size; - pdl_state = GET_PROTOCOL_ID; + protokoll_descriptor_list_state = GET_PROTOCOL_ID; break; @@ -146,11 +241,11 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ if (protocol_offset >= protocol_size){ - pdl_state = GET_PROTOCOL_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH; break; - } - - pdl_state = GET_PROTOCOL_VALUE_LENGTH; + } + + protokoll_descriptor_list_state = GET_PROTOCOL_VALUE_LENGTH; protocol_value_bytes_received = 0; break; @@ -160,7 +255,7 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ if (!de_state_size(data, &de_header_state)) break; protocol_value_size = de_header_state.de_size; - pdl_state = GET_PROTOCOL_VALUE; + protokoll_descriptor_list_state = GET_PROTOCOL_VALUE; sdp_rfcomm_channel_nr = 0; break; @@ -176,11 +271,11 @@ static void sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(uint32_ } if (protocol_offset >= protocol_size) { - pdl_state = GET_PROTOCOL_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_LENGTH; break; } - pdl_state = GET_PROTOCOL_ID_HEADER_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_ID_HEADER_LENGTH; break; default: break; @@ -191,14 +286,15 @@ static void sdp_client_query_rfcomm_handle_service_name_data(uint32_t attribute_ // Get Header Len if (data_offset == 0){ - de_state_size(data, &sn_de_header_state); - sdp_service_name_header_size = sn_de_header_state.addon_header_bytes + 1; + de_state_init(&de_header_state); + de_state_size(data, &de_header_state); + sdp_service_name_header_size = de_header_state.addon_header_bytes + 1; return; } // Get Header if (data_offset < sdp_service_name_header_size){ - de_state_size(data, &sn_de_header_state); + de_state_size(data, &de_header_state); return; } @@ -225,7 +321,7 @@ static void sdp_client_query_rfcomm_handle_service_name_data(uint32_t attribute_ // notify on last char if ((data_offset == (attribute_value_length - 1)) && (sdp_rfcomm_channel_nr!=0)){ - sdp_rfcomm_query_emit_service(); + sdp_client_query_rfcomm_handle_record_parsed(); } } @@ -235,17 +331,20 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type, switch (hci_event_packet_get_type(packet)){ case SDP_EVENT_QUERY_SERVICE_RECORD_HANDLE: - // handle service without a name - if (sdp_rfcomm_channel_nr){ - sdp_rfcomm_query_emit_service(); - } + sdp_client_query_rfcomm_handle_record_parsed(); // prepare for new record - sdp_rfcomm_channel_nr = 0; - sdp_service_name[0] = 0; + sdp_rfcomm_query_prepare(); break; case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: switch (sdp_event_query_attribute_byte_get_attribute_id(packet)){ + case BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST: + if (sdp_client_rfcomm_match_serviceclass){ + sdp_client_query_rfcomm_handle_service_class_list_data(sdp_event_query_attribute_byte_get_attribute_length(packet), + sdp_event_query_attribute_byte_get_data_offset(packet), + sdp_event_query_attribute_byte_get_data(packet)); + } + break; case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: // find rfcomm channel sdp_client_query_rfcomm_handle_protocol_descriptor_list_data(sdp_event_query_attribute_byte_get_attribute_length(packet), @@ -264,11 +363,8 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type, } break; case SDP_EVENT_QUERY_COMPLETE: - // handle service without a name - if (sdp_rfcomm_channel_nr){ - sdp_rfcomm_query_emit_service(); - } - (*sdp_app_callback)(HCI_EVENT_PACKET, 0, packet, size); + sdp_client_query_rfcomm_handle_record_parsed(); + (*sdp_app_callback)(HCI_EVENT_PACKET, 0, packet, size); break; default: break; @@ -277,30 +373,41 @@ static void sdp_client_query_rfcomm_handle_sdp_parser_event(uint8_t packet_type, void sdp_client_query_rfcomm_init(void){ // init - de_state_init(&de_header_state); - de_state_init(&sn_de_header_state); - pdl_state = GET_PROTOCOL_LIST_LENGTH; + protokoll_descriptor_list_state = GET_PROTOCOL_LIST_LENGTH; protocol_offset = 0; sdp_rfcomm_channel_nr = 0; sdp_service_name[0] = 0; } -// Public API - -uint8_t sdp_client_query_rfcomm_channel_and_name_for_search_pattern(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){ - if (!sdp_client_ready()) return SDP_QUERY_BUSY; - +static uint8_t sdp_client_query_rfcomm(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){ sdp_app_callback = callback; sdp_client_query_rfcomm_init(); return sdp_client_query(&sdp_client_query_rfcomm_handle_sdp_parser_event, remote, service_search_pattern, (uint8_t*)&des_attributeIDList[0]); } +// Public API + uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16){ if (!sdp_client_ready()) return SDP_QUERY_BUSY; - return sdp_client_query_rfcomm_channel_and_name_for_search_pattern(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16)); + sdp_client_rfcomm_match_serviceclass = false; + return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16)); +} + +uint8_t sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16){ + if (!sdp_client_ready()) return SDP_QUERY_BUSY; + sdp_client_rfcomm_match_serviceclass = true; + sdp_client_rfcomm_uuid16 = uuid16; + return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid16(uuid16)); } uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid128(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * uuid128){ if (!sdp_client_ready()) return SDP_QUERY_BUSY; - return sdp_client_query_rfcomm_channel_and_name_for_search_pattern(callback, remote, sdp_service_search_pattern_for_uuid128(uuid128)); + sdp_client_rfcomm_match_serviceclass = false; + return sdp_client_query_rfcomm(callback, remote, sdp_service_search_pattern_for_uuid128(uuid128)); +} + +uint8_t sdp_client_query_rfcomm_channel_and_name_for_search_pattern(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * service_search_pattern){ + if (!sdp_client_ready()) return SDP_QUERY_BUSY; + sdp_client_rfcomm_match_serviceclass = false; + return sdp_client_query_rfcomm(callback, remote, service_search_pattern); } diff --git a/src/classic/sdp_client_rfcomm.h b/src/classic/sdp_client_rfcomm.h index e0c04b488..c8c944e4c 100644 --- a/src/classic/sdp_client_rfcomm.h +++ b/src/classic/sdp_client_rfcomm.h @@ -53,14 +53,29 @@ extern "C" { /* API_START */ /** - * @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID. + * @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID anywhere. * @note calls sdp_service_search_pattern_for_uuid16 that uses global buffer + * @param callback handler + * @param remote BD_ADDR + * @param uuid16 */ -uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid); + uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16); -/** - * @brief Searches SDP records on a remote device for RFCOMM services with a given 128-bit UUID. +/** + * @brief Searches SDP records on a remote device for RFCOMM services with a given 16-bit UUID in its ServiceClassIDList + * @note calls sdp_service_search_pattern_for_uuid16 that uses global buffer + * @param callback handler + * @param remote BD_ADDR + * @param uuid16 + */ +uint8_t sdp_client_query_rfcomm_channel_and_name_for_service_class_uuid(btstack_packet_handler_t callback, bd_addr_t remote, uint16_t uuid16); + +/** + * @brief Searches SDP records on a remote device for RFCOMM services with a given 128-bit UUID anywhere * @note calls sdp_service_search_pattern_for_uuid128 that uses global buffer + * @param callback handler + * @param remote BD_ADDR + * @param uuid128 */ uint8_t sdp_client_query_rfcomm_channel_and_name_for_uuid128(btstack_packet_handler_t callback, bd_addr_t remote, const uint8_t * uuid128); diff --git a/test/sdp_client/sdp_rfcomm_query.c b/test/sdp_client/sdp_rfcomm_query.c index 73bc767de..2cf1f7b15 100644 --- a/test/sdp_client/sdp_rfcomm_query.c +++ b/test/sdp_client/sdp_rfcomm_query.c @@ -15,6 +15,7 @@ #include "btstack_event.h" #include "btstack_memory.h" #include "btstack_run_loop.h" +#include "bluetooth_sdp.h" #include "classic/sdp_client_rfcomm.h" #include "classic/sdp_util.h" #include "classic/spp_server.h" @@ -109,19 +110,19 @@ TEST_GROUP(SDPClient){ void setup(void){ service_index = 0; sdp_client_reset(); // avoid "not ready" warning - // start query using public API although data will be injected - sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, address, 0x1234); } }; TEST(SDPClient, QueryRFCOMMWithSyntheticData){ - const char* expected_name[] = {"SDP Response Test1", "SDP Response Test2", "SDP Response Test3"}; uint8_t expected_channel[] = {10, 11, 12}; int record_nr = sizeof(expected_channel)/sizeof(uint8_t); int i; - + + // start query using public API although data will be injected + sdp_client_query_rfcomm_channel_and_name_for_uuid(&handle_query_rfcomm_event, address, 0x1234); + de_create_sequence(spp_buffer); for (i=0; i