1746ccb7eSMatthias Ringwald /* 2746ccb7eSMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3746ccb7eSMatthias Ringwald * 4746ccb7eSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5746ccb7eSMatthias Ringwald * modification, are permitted provided that the following conditions 6746ccb7eSMatthias Ringwald * are met: 7746ccb7eSMatthias Ringwald * 8746ccb7eSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9746ccb7eSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10746ccb7eSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11746ccb7eSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12746ccb7eSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13746ccb7eSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14746ccb7eSMatthias Ringwald * contributors may be used to endorse or promote products derived 15746ccb7eSMatthias Ringwald * from this software without specific prior written permission. 16746ccb7eSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17746ccb7eSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18746ccb7eSMatthias Ringwald * monetary gain. 19746ccb7eSMatthias Ringwald * 20746ccb7eSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21746ccb7eSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22746ccb7eSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25746ccb7eSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26746ccb7eSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27746ccb7eSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28746ccb7eSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29746ccb7eSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30746ccb7eSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31746ccb7eSMatthias Ringwald * SUCH DAMAGE. 32746ccb7eSMatthias Ringwald * 33746ccb7eSMatthias Ringwald * Please inquire about commercial licensing options at 34746ccb7eSMatthias Ringwald * [email protected] 35746ccb7eSMatthias Ringwald * 36746ccb7eSMatthias Ringwald */ 37746ccb7eSMatthias Ringwald 38e501bae0SMatthias Ringwald #define BTSTACK_FILE__ "sdp_server.c" 39ab2c6ae4SMatthias Ringwald 40746ccb7eSMatthias Ringwald /* 41746ccb7eSMatthias Ringwald * Implementation of the Service Discovery Protocol Server 42746ccb7eSMatthias Ringwald */ 43746ccb7eSMatthias Ringwald 44746ccb7eSMatthias Ringwald #include <string.h> 45746ccb7eSMatthias Ringwald 4603751aa7SMatthias Ringwald #include "bluetooth.h" 4784e3541eSMilanka Ringwald #include "bluetooth_psm.h" 48235946f1SMatthias Ringwald #include "bluetooth_sdp.h" 49746ccb7eSMatthias Ringwald #include "btstack_debug.h" 500e2df43fSMatthias Ringwald #include "btstack_event.h" 5159c6af15SMatthias Ringwald #include "btstack_memory.h" 5259c6af15SMatthias Ringwald #include "classic/core.h" 53746ccb7eSMatthias Ringwald #include "classic/sdp_server.h" 54746ccb7eSMatthias Ringwald #include "classic/sdp_util.h" 5503751aa7SMatthias Ringwald #include "hci.h" 5684e3541eSMilanka Ringwald #include "hci_dump.h" 5759c6af15SMatthias Ringwald #include "l2cap.h" 58746ccb7eSMatthias Ringwald 597616f654SMatthias Ringwald // max number of incoming l2cap connections that can be queued instead of getting rejected 607616f654SMatthias Ringwald #ifndef SDP_WAITING_LIST_MAX_COUNT 617616f654SMatthias Ringwald #define SDP_WAITING_LIST_MAX_COUNT 8 627616f654SMatthias Ringwald #endif 637616f654SMatthias Ringwald 64746ccb7eSMatthias Ringwald // max reserved ServiceRecordHandle 65f20b4214SMatthias Ringwald #define MAX_RESERVED_SERVICE_RECORD_HANDLE 0xffff 66746ccb7eSMatthias Ringwald 67746ccb7eSMatthias Ringwald // max SDP response matches L2CAP PDU -- allow to use smaller buffer 68746ccb7eSMatthias Ringwald #ifndef SDP_RESPONSE_BUFFER_SIZE 6903751aa7SMatthias Ringwald #define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_PAYLOAD_SIZE-L2CAP_HEADER_SIZE) 70746ccb7eSMatthias Ringwald #endif 71746ccb7eSMatthias Ringwald 72746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 73746ccb7eSMatthias Ringwald 74746ccb7eSMatthias Ringwald // registered service records 75f20b4214SMatthias Ringwald static btstack_linked_list_t sdp_server_service_records; 76746ccb7eSMatthias Ringwald 77746ccb7eSMatthias Ringwald // our handles start after the reserved range 78f20b4214SMatthias Ringwald static uint32_t sdp_server_next_service_record_handle; 79746ccb7eSMatthias Ringwald 80746ccb7eSMatthias Ringwald static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE]; 81746ccb7eSMatthias Ringwald 82f20b4214SMatthias Ringwald static uint16_t sdp_server_l2cap_cid; 83f20b4214SMatthias Ringwald static uint16_t sdp_server_response_size; 84f20b4214SMatthias Ringwald static uint16_t sdp_server_l2cap_waiting_list_cids[SDP_WAITING_LIST_MAX_COUNT]; 85f20b4214SMatthias Ringwald static int sdp_server_l2cap_waiting_list_count; 86746ccb7eSMatthias Ringwald 8722d58ff8SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 8822d58ff8SMatthias Ringwald static bool sdp_server_testing_single_record_reponse = false; 8922d58ff8SMatthias Ringwald #endif 9022d58ff8SMatthias Ringwald 91746ccb7eSMatthias Ringwald void sdp_init(void){ 92f20b4214SMatthias Ringwald sdp_server_next_service_record_handle = ((uint32_t) MAX_RESERVED_SERVICE_RECORD_HANDLE) + 2; 93746ccb7eSMatthias Ringwald // register with l2cap psm sevices - max MTU 9484e3541eSMilanka Ringwald l2cap_register_service(sdp_packet_handler, BLUETOOTH_PSM_SDP, 0xffff, LEVEL_0); 950396d6ccSMatthias Ringwald } 960396d6ccSMatthias Ringwald 970396d6ccSMatthias Ringwald void sdp_deinit(void){ 98f20b4214SMatthias Ringwald sdp_server_service_records = NULL; 99f20b4214SMatthias Ringwald sdp_server_l2cap_cid = 0; 100f20b4214SMatthias Ringwald sdp_server_response_size = 0; 101f20b4214SMatthias Ringwald sdp_server_l2cap_waiting_list_count = 0; 102746ccb7eSMatthias Ringwald } 103746ccb7eSMatthias Ringwald 104746ccb7eSMatthias Ringwald uint32_t sdp_get_service_record_handle(const uint8_t * record){ 105746ccb7eSMatthias Ringwald // TODO: make sdp_get_attribute_value_for_attribute_id accept const data to remove cast 106235946f1SMatthias Ringwald uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id((uint8_t *)record, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE); 107746ccb7eSMatthias Ringwald if (!serviceRecordHandleAttribute) return 0; 108746ccb7eSMatthias Ringwald if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0; 109746ccb7eSMatthias Ringwald if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0; 110c9b8fdd9SMatthias Ringwald return big_endian_read_32(serviceRecordHandleAttribute, 1); 111746ccb7eSMatthias Ringwald } 112746ccb7eSMatthias Ringwald 113746ccb7eSMatthias Ringwald static service_record_item_t * sdp_get_record_item_for_handle(uint32_t handle){ 114746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 115f20b4214SMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_server_service_records; it ; it = it->next){ 116746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 117746ccb7eSMatthias Ringwald if (item->service_record_handle == handle){ 118746ccb7eSMatthias Ringwald return item; 119746ccb7eSMatthias Ringwald } 120746ccb7eSMatthias Ringwald } 121746ccb7eSMatthias Ringwald return NULL; 122746ccb7eSMatthias Ringwald } 123746ccb7eSMatthias Ringwald 124746ccb7eSMatthias Ringwald uint8_t * sdp_get_record_for_handle(uint32_t handle){ 125746ccb7eSMatthias Ringwald service_record_item_t * record_item = sdp_get_record_item_for_handle(handle); 1266e791780SMatthias Ringwald if (!record_item) return NULL; 127746ccb7eSMatthias Ringwald return record_item->service_record; 128746ccb7eSMatthias Ringwald } 129746ccb7eSMatthias Ringwald 130746ccb7eSMatthias Ringwald // get next free, unregistered service record handle 131746ccb7eSMatthias Ringwald uint32_t sdp_create_service_record_handle(void){ 132746ccb7eSMatthias Ringwald uint32_t handle = 0; 133746ccb7eSMatthias Ringwald do { 134f20b4214SMatthias Ringwald handle = sdp_server_next_service_record_handle++; 135746ccb7eSMatthias Ringwald if (sdp_get_record_item_for_handle(handle)) handle = 0; 136746ccb7eSMatthias Ringwald } while (handle == 0); 137746ccb7eSMatthias Ringwald return handle; 138746ccb7eSMatthias Ringwald } 139746ccb7eSMatthias Ringwald 140746ccb7eSMatthias Ringwald /** 141746ccb7eSMatthias Ringwald * @brief Register Service Record with database using ServiceRecordHandle stored in record 142746ccb7eSMatthias Ringwald * @pre AttributeIDs are in ascending order 143746ccb7eSMatthias Ringwald * @pre ServiceRecordHandle is first attribute and valid 144746ccb7eSMatthias Ringwald * @param record is not copied! 145746ccb7eSMatthias Ringwald * @result status 146746ccb7eSMatthias Ringwald */ 147746ccb7eSMatthias Ringwald uint8_t sdp_register_service(const uint8_t * record){ 148746ccb7eSMatthias Ringwald 149746ccb7eSMatthias Ringwald // validate service record handle. it must: exist, be in valid range, not have been already used 150746ccb7eSMatthias Ringwald uint32_t record_handle = sdp_get_service_record_handle(record); 151746ccb7eSMatthias Ringwald if (!record_handle) return SDP_HANDLE_INVALID; 152f20b4214SMatthias Ringwald if (record_handle <= MAX_RESERVED_SERVICE_RECORD_HANDLE) return SDP_HANDLE_INVALID; 153746ccb7eSMatthias Ringwald if (sdp_get_record_item_for_handle(record_handle)) return SDP_HANDLE_ALREADY_REGISTERED; 154746ccb7eSMatthias Ringwald 155746ccb7eSMatthias Ringwald // alloc memory for new service_record_item 156746ccb7eSMatthias Ringwald service_record_item_t * newRecordItem = btstack_memory_service_record_item_get(); 157746ccb7eSMatthias Ringwald if (!newRecordItem) return BTSTACK_MEMORY_ALLOC_FAILED; 158746ccb7eSMatthias Ringwald 159746ccb7eSMatthias Ringwald // set handle and record 160746ccb7eSMatthias Ringwald newRecordItem->service_record_handle = record_handle; 161746ccb7eSMatthias Ringwald newRecordItem->service_record = (uint8_t*) record; 162746ccb7eSMatthias Ringwald 163746ccb7eSMatthias Ringwald // add to linked list 164f20b4214SMatthias Ringwald btstack_linked_list_add(&sdp_server_service_records, (btstack_linked_item_t *) newRecordItem); 165746ccb7eSMatthias Ringwald 166746ccb7eSMatthias Ringwald return 0; 167746ccb7eSMatthias Ringwald } 168746ccb7eSMatthias Ringwald 169746ccb7eSMatthias Ringwald // 170746ccb7eSMatthias Ringwald // unregister service record 171746ccb7eSMatthias Ringwald // 172746ccb7eSMatthias Ringwald void sdp_unregister_service(uint32_t service_record_handle){ 173746ccb7eSMatthias Ringwald service_record_item_t * record_item = sdp_get_record_item_for_handle(service_record_handle); 174746ccb7eSMatthias Ringwald if (!record_item) return; 175f20b4214SMatthias Ringwald btstack_linked_list_remove(&sdp_server_service_records, (btstack_linked_item_t *) record_item); 176c0a6fc5dSMatthias Ringwald btstack_memory_service_record_item_free(record_item); 177746ccb7eSMatthias Ringwald } 178746ccb7eSMatthias Ringwald 179746ccb7eSMatthias Ringwald // PDU 180746ccb7eSMatthias Ringwald // PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, .. 181746ccb7eSMatthias Ringwald 182746ccb7eSMatthias Ringwald static int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){ 183746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ErrorResponse; 184746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 185746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, 2); 186746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, error_code); // invalid syntax 187746ccb7eSMatthias Ringwald return 7; 188746ccb7eSMatthias Ringwald } 189746ccb7eSMatthias Ringwald 190746ccb7eSMatthias Ringwald int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){ 191746ccb7eSMatthias Ringwald 192746ccb7eSMatthias Ringwald // get request details 193746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 1944443af49SMatthias Ringwald uint16_t param_len = big_endian_read_16(packet, 3); 195746ccb7eSMatthias Ringwald uint8_t * serviceSearchPattern = &packet[5]; 1964443af49SMatthias Ringwald uint16_t serviceSearchPatternLen = de_get_len_safe(serviceSearchPattern, param_len); 197*cc643af3SMatthias Ringwald if (sdp_valid_service_search_pattern(serviceSearchPattern) == false){ 198*cc643af3SMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0003); /// invalid request syntax 199*cc643af3SMatthias Ringwald } 2004443af49SMatthias Ringwald // assert service search pattern is contained 2014443af49SMatthias Ringwald if (!serviceSearchPatternLen) return 0; 2024443af49SMatthias Ringwald param_len -= serviceSearchPatternLen; 2034443af49SMatthias Ringwald // assert max record count is contained 2044443af49SMatthias Ringwald if (param_len < 2) return 0; 205746ccb7eSMatthias Ringwald uint16_t maximumServiceRecordCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen); 2064443af49SMatthias Ringwald param_len -= 2; 2074443af49SMatthias Ringwald // assert continuation state len is contained in param_len 2084443af49SMatthias Ringwald if (param_len < 1) return 0; 209746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2]; 2104443af49SMatthias Ringwald // assert continuation state is contained in param_len 211c1ab6cc1SMatthias Ringwald if ((1 + continuationState[0]) > param_len) return 0; 212746ccb7eSMatthias Ringwald 2134443af49SMatthias Ringwald // calc maximumServiceRecordCount based on remote MTU 214746ccb7eSMatthias Ringwald uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4; 21522d58ff8SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 21622d58ff8SMatthias Ringwald if (sdp_server_testing_single_record_reponse){ 21722d58ff8SMatthias Ringwald maxNrServiceRecordsPerResponse = 1; 21822d58ff8SMatthias Ringwald } 21922d58ff8SMatthias Ringwald #endif 220746ccb7eSMatthias Ringwald 221746ccb7eSMatthias Ringwald // continuation state contains index of next service record to examine 222746ccb7eSMatthias Ringwald int continuation = 0; 223746ccb7eSMatthias Ringwald uint16_t continuation_index = 0; 224746ccb7eSMatthias Ringwald if (continuationState[0] == 2){ 225746ccb7eSMatthias Ringwald continuation_index = big_endian_read_16(continuationState, 1); 226746ccb7eSMatthias Ringwald } 227746ccb7eSMatthias Ringwald 228746ccb7eSMatthias Ringwald // get and limit total count 229746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 230746ccb7eSMatthias Ringwald uint16_t total_service_count = 0; 231f20b4214SMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_server_service_records; it ; it = it->next){ 232746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 233746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 234746ccb7eSMatthias Ringwald total_service_count++; 235746ccb7eSMatthias Ringwald } 236746ccb7eSMatthias Ringwald if (total_service_count > maximumServiceRecordCount){ 237746ccb7eSMatthias Ringwald total_service_count = maximumServiceRecordCount; 238746ccb7eSMatthias Ringwald } 239746ccb7eSMatthias Ringwald 240746ccb7eSMatthias Ringwald // ServiceRecordHandleList at 9 241746ccb7eSMatthias Ringwald uint16_t pos = 9; 242746ccb7eSMatthias Ringwald uint16_t current_service_count = 0; 243746ccb7eSMatthias Ringwald uint16_t current_service_index = 0; 244746ccb7eSMatthias Ringwald uint16_t matching_service_count = 0; 245f20b4214SMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_server_service_records; it ; it = it->next, ++current_service_index){ 246746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 247746ccb7eSMatthias Ringwald 248746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 249746ccb7eSMatthias Ringwald matching_service_count++; 250746ccb7eSMatthias Ringwald 251746ccb7eSMatthias Ringwald if (current_service_index < continuation_index) continue; 252746ccb7eSMatthias Ringwald 253746ccb7eSMatthias Ringwald big_endian_store_32(sdp_response_buffer, pos, item->service_record_handle); 254746ccb7eSMatthias Ringwald pos += 4; 255746ccb7eSMatthias Ringwald current_service_count++; 256746ccb7eSMatthias Ringwald 257746ccb7eSMatthias Ringwald if (matching_service_count >= total_service_count) break; 258746ccb7eSMatthias Ringwald 259746ccb7eSMatthias Ringwald if (current_service_count >= maxNrServiceRecordsPerResponse){ 260746ccb7eSMatthias Ringwald continuation = 1; 261746ccb7eSMatthias Ringwald continuation_index = current_service_index + 1; 262746ccb7eSMatthias Ringwald break; 263746ccb7eSMatthias Ringwald } 264746ccb7eSMatthias Ringwald } 265746ccb7eSMatthias Ringwald 266746ccb7eSMatthias Ringwald // Store continuation state 267746ccb7eSMatthias Ringwald if (continuation) { 268746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 2; 269746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_index); 270746ccb7eSMatthias Ringwald pos += 2; 271746ccb7eSMatthias Ringwald } else { 272746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 273746ccb7eSMatthias Ringwald } 274746ccb7eSMatthias Ringwald 275746ccb7eSMatthias Ringwald // header 276746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceSearchResponse; 277746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 278746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 279746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, total_service_count); 280746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 7, current_service_count); 281746ccb7eSMatthias Ringwald 282746ccb7eSMatthias Ringwald return pos; 283746ccb7eSMatthias Ringwald } 284746ccb7eSMatthias Ringwald 285746ccb7eSMatthias Ringwald int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){ 286746ccb7eSMatthias Ringwald 287746ccb7eSMatthias Ringwald // get request details 288746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 2894443af49SMatthias Ringwald uint16_t param_len = big_endian_read_16(packet, 3); 2904443af49SMatthias Ringwald // assert serviceRecordHandle and maximumAttributeByteCount are in param_len 2914443af49SMatthias Ringwald if (param_len < 6) return 0; 292c9b8fdd9SMatthias Ringwald uint32_t serviceRecordHandle = big_endian_read_32(packet, 5); 293746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount = big_endian_read_16(packet, 9); 2944443af49SMatthias Ringwald param_len -= 6; 295746ccb7eSMatthias Ringwald uint8_t * attributeIDList = &packet[11]; 2964443af49SMatthias Ringwald uint16_t attributeIDListLen = de_get_len_safe(attributeIDList, param_len); 2972b1502ceSMatthias Ringwald if (!sdp_attribute_list_valid(attributeIDList) == false){ 2982b1502ceSMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0003); /// invalid request syntax 2992b1502ceSMatthias Ringwald } 3004443af49SMatthias Ringwald // assert attributeIDList are in param_len 3014443af49SMatthias Ringwald if (!attributeIDListLen) return 0; 3024443af49SMatthias Ringwald param_len -= attributeIDListLen; 3034443af49SMatthias Ringwald // assert continuation state len is contained in param_len 3044443af49SMatthias Ringwald if (param_len < 1) return 0; 305746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[11+attributeIDListLen]; 3064443af49SMatthias Ringwald // assert continuation state is contained in param_len 307c1ab6cc1SMatthias Ringwald if ((1 + continuationState[0]) > param_len) return 0; 308746ccb7eSMatthias Ringwald 309746ccb7eSMatthias Ringwald // calc maximumAttributeByteCount based on remote MTU 310746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3); 311746ccb7eSMatthias Ringwald if (maximumAttributeByteCount2 < maximumAttributeByteCount) { 312746ccb7eSMatthias Ringwald maximumAttributeByteCount = maximumAttributeByteCount2; 313746ccb7eSMatthias Ringwald } 314746ccb7eSMatthias Ringwald 315746ccb7eSMatthias Ringwald // continuation state contains the offset into the complete response 316746ccb7eSMatthias Ringwald uint16_t continuation_offset = 0; 317746ccb7eSMatthias Ringwald if (continuationState[0] == 2){ 318746ccb7eSMatthias Ringwald continuation_offset = big_endian_read_16(continuationState, 1); 319746ccb7eSMatthias Ringwald } 320746ccb7eSMatthias Ringwald 321746ccb7eSMatthias Ringwald // get service record 322746ccb7eSMatthias Ringwald service_record_item_t * item = sdp_get_record_item_for_handle(serviceRecordHandle); 323746ccb7eSMatthias Ringwald if (!item){ 324746ccb7eSMatthias Ringwald // service record handle doesn't exist 325746ccb7eSMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle 326746ccb7eSMatthias Ringwald } 327746ccb7eSMatthias Ringwald 328746ccb7eSMatthias Ringwald 329746ccb7eSMatthias Ringwald // AttributeList - starts at offset 7 330746ccb7eSMatthias Ringwald uint16_t pos = 7; 331746ccb7eSMatthias Ringwald 332746ccb7eSMatthias Ringwald if (continuation_offset == 0){ 333746ccb7eSMatthias Ringwald 334746ccb7eSMatthias Ringwald // get size of this record 335839ee6d9SMatthias Ringwald uint16_t filtered_attributes_size = sdp_get_filtered_size(item->service_record, attributeIDList); 336746ccb7eSMatthias Ringwald 337746ccb7eSMatthias Ringwald // store DES 338746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); 339746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 340746ccb7eSMatthias Ringwald pos += 3; 341746ccb7eSMatthias Ringwald } 342746ccb7eSMatthias Ringwald 343746ccb7eSMatthias Ringwald // copy maximumAttributeByteCount from record 344746ccb7eSMatthias Ringwald uint16_t bytes_used; 345746ccb7eSMatthias Ringwald int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); 346746ccb7eSMatthias Ringwald pos += bytes_used; 347746ccb7eSMatthias Ringwald 348746ccb7eSMatthias Ringwald uint16_t attributeListByteCount = pos - 7; 349746ccb7eSMatthias Ringwald 350746ccb7eSMatthias Ringwald if (complete) { 351746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 352746ccb7eSMatthias Ringwald } else { 353746ccb7eSMatthias Ringwald continuation_offset += bytes_used; 354746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 2; 355746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_offset); 356746ccb7eSMatthias Ringwald pos += 2; 357746ccb7eSMatthias Ringwald } 358746ccb7eSMatthias Ringwald 359746ccb7eSMatthias Ringwald // header 360746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceAttributeResponse; 361746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 362746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 363746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, attributeListByteCount); 364746ccb7eSMatthias Ringwald 365746ccb7eSMatthias Ringwald return pos; 366746ccb7eSMatthias Ringwald } 367746ccb7eSMatthias Ringwald 368746ccb7eSMatthias Ringwald static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){ 369746ccb7eSMatthias Ringwald uint16_t total_response_size = 0; 370746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 371f20b4214SMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_server_service_records; it ; it = it->next){ 372746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 373746ccb7eSMatthias Ringwald 374746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 375746ccb7eSMatthias Ringwald 376746ccb7eSMatthias Ringwald // for all service records that match 377839ee6d9SMatthias Ringwald total_response_size += 3 + sdp_get_filtered_size(item->service_record, attributeIDList); 378746ccb7eSMatthias Ringwald } 379746ccb7eSMatthias Ringwald return total_response_size; 380746ccb7eSMatthias Ringwald } 381746ccb7eSMatthias Ringwald 382746ccb7eSMatthias Ringwald int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){ 383746ccb7eSMatthias Ringwald 384746ccb7eSMatthias Ringwald // SDP header before attribute sevice list: 7 385746ccb7eSMatthias Ringwald // Continuation, worst case: 5 386746ccb7eSMatthias Ringwald 387746ccb7eSMatthias Ringwald // get request details 388746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 3894443af49SMatthias Ringwald uint16_t param_len = big_endian_read_16(packet, 3); 390746ccb7eSMatthias Ringwald uint8_t * serviceSearchPattern = &packet[5]; 3914443af49SMatthias Ringwald uint16_t serviceSearchPatternLen = de_get_len_safe(serviceSearchPattern, param_len); 392*cc643af3SMatthias Ringwald if (sdp_valid_service_search_pattern(serviceSearchPattern) == false){ 393*cc643af3SMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0003); /// invalid request syntax 394*cc643af3SMatthias Ringwald } 3954443af49SMatthias Ringwald // assert serviceSearchPattern header is contained in param_len 3964443af49SMatthias Ringwald if (!serviceSearchPatternLen) return 0; 3974443af49SMatthias Ringwald param_len -= serviceSearchPatternLen; 3984443af49SMatthias Ringwald // assert maximumAttributeByteCount contained in param_len 3994443af49SMatthias Ringwald if (param_len < 2) return 0; 400746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen); 4014443af49SMatthias Ringwald param_len -= 2; 402746ccb7eSMatthias Ringwald uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2]; 4034443af49SMatthias Ringwald uint16_t attributeIDListLen = de_get_len_safe(attributeIDList, param_len); 4042b1502ceSMatthias Ringwald if (!sdp_attribute_list_valid(attributeIDList) == false){ 4052b1502ceSMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0003); /// invalid request syntax 4062b1502ceSMatthias Ringwald } 4074443af49SMatthias Ringwald // assert attributeIDList is contained in param_len 4084443af49SMatthias Ringwald if (!attributeIDListLen) return 0; 4094443af49SMatthias Ringwald // assert continuation state len is contained in param_len 4104443af49SMatthias Ringwald if (param_len < 1) return 0; 411746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen]; 4124443af49SMatthias Ringwald // assert continuation state is contained in param_len 413c1ab6cc1SMatthias Ringwald if ((1 + continuationState[0]) > param_len) return 0; 414746ccb7eSMatthias Ringwald 415746ccb7eSMatthias Ringwald // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block 416746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount2 = remote_mtu - 12; 417746ccb7eSMatthias Ringwald if (maximumAttributeByteCount2 < maximumAttributeByteCount) { 418746ccb7eSMatthias Ringwald maximumAttributeByteCount = maximumAttributeByteCount2; 419746ccb7eSMatthias Ringwald } 420746ccb7eSMatthias Ringwald 421746ccb7eSMatthias Ringwald // continuation state contains: index of next service record to examine 422746ccb7eSMatthias Ringwald // continuation state contains: byte offset into this service record 423746ccb7eSMatthias Ringwald uint16_t continuation_service_index = 0; 424746ccb7eSMatthias Ringwald uint16_t continuation_offset = 0; 425746ccb7eSMatthias Ringwald if (continuationState[0] == 4){ 426746ccb7eSMatthias Ringwald continuation_service_index = big_endian_read_16(continuationState, 1); 427746ccb7eSMatthias Ringwald continuation_offset = big_endian_read_16(continuationState, 3); 428746ccb7eSMatthias Ringwald } 429746ccb7eSMatthias Ringwald 430746ccb7eSMatthias Ringwald // log_info("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u", continuation_service_index, continuation_offset, maximumAttributeByteCount); 431746ccb7eSMatthias Ringwald 432746ccb7eSMatthias Ringwald // AttributeLists - starts at offset 7 433746ccb7eSMatthias Ringwald uint16_t pos = 7; 434746ccb7eSMatthias Ringwald 435746ccb7eSMatthias Ringwald // add DES with total size for first request 436c1ab6cc1SMatthias Ringwald if ((continuation_service_index == 0) && (continuation_offset == 0)){ 437746ccb7eSMatthias Ringwald uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList); 438746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size); 439746ccb7eSMatthias Ringwald // log_info("total response size %u", total_response_size); 440746ccb7eSMatthias Ringwald pos += 3; 441746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 442746ccb7eSMatthias Ringwald } 443746ccb7eSMatthias Ringwald 444746ccb7eSMatthias Ringwald // create attribute list 445746ccb7eSMatthias Ringwald int first_answer = 1; 446746ccb7eSMatthias Ringwald int continuation = 0; 447746ccb7eSMatthias Ringwald uint16_t current_service_index = 0; 448f20b4214SMatthias Ringwald btstack_linked_item_t *it = (btstack_linked_item_t *) sdp_server_service_records; 449746ccb7eSMatthias Ringwald for ( ; it ; it = it->next, ++current_service_index){ 450746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 451746ccb7eSMatthias Ringwald 452746ccb7eSMatthias Ringwald if (current_service_index < continuation_service_index ) continue; 453746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 454746ccb7eSMatthias Ringwald 455746ccb7eSMatthias Ringwald if (continuation_offset == 0){ 456746ccb7eSMatthias Ringwald 457746ccb7eSMatthias Ringwald // get size of this record 458839ee6d9SMatthias Ringwald uint16_t filtered_attributes_size = sdp_get_filtered_size(item->service_record, attributeIDList); 459746ccb7eSMatthias Ringwald 460746ccb7eSMatthias Ringwald // stop if complete record doesn't fits into response but we already have a partial response 461c1ab6cc1SMatthias Ringwald if (((filtered_attributes_size + 3) > maximumAttributeByteCount) && !first_answer) { 462746ccb7eSMatthias Ringwald continuation = 1; 463746ccb7eSMatthias Ringwald break; 464746ccb7eSMatthias Ringwald } 465746ccb7eSMatthias Ringwald 466746ccb7eSMatthias Ringwald // store DES 467746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); 468746ccb7eSMatthias Ringwald pos += 3; 469746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 470746ccb7eSMatthias Ringwald } 471746ccb7eSMatthias Ringwald 472746ccb7eSMatthias Ringwald first_answer = 0; 473746ccb7eSMatthias Ringwald 474746ccb7eSMatthias Ringwald // copy maximumAttributeByteCount from record 475746ccb7eSMatthias Ringwald uint16_t bytes_used; 476746ccb7eSMatthias Ringwald int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); 477746ccb7eSMatthias Ringwald pos += bytes_used; 478746ccb7eSMatthias Ringwald maximumAttributeByteCount -= bytes_used; 479746ccb7eSMatthias Ringwald 480746ccb7eSMatthias Ringwald if (complete) { 481746ccb7eSMatthias Ringwald continuation_offset = 0; 482746ccb7eSMatthias Ringwald continue; 483746ccb7eSMatthias Ringwald } 484746ccb7eSMatthias Ringwald 485746ccb7eSMatthias Ringwald continuation = 1; 486746ccb7eSMatthias Ringwald continuation_offset += bytes_used; 487746ccb7eSMatthias Ringwald break; 488746ccb7eSMatthias Ringwald } 489746ccb7eSMatthias Ringwald 490746ccb7eSMatthias Ringwald uint16_t attributeListsByteCount = pos - 7; 491746ccb7eSMatthias Ringwald 492746ccb7eSMatthias Ringwald // Continuation State 493746ccb7eSMatthias Ringwald if (continuation){ 494746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 4; 495746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index); 496746ccb7eSMatthias Ringwald pos += 2; 497746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_offset); 498746ccb7eSMatthias Ringwald pos += 2; 499746ccb7eSMatthias Ringwald } else { 500746ccb7eSMatthias Ringwald // complete 501746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 502746ccb7eSMatthias Ringwald } 503746ccb7eSMatthias Ringwald 504746ccb7eSMatthias Ringwald // create SDP header 505746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse; 506746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 507746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 508746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, attributeListsByteCount); 509746ccb7eSMatthias Ringwald 510746ccb7eSMatthias Ringwald return pos; 511746ccb7eSMatthias Ringwald } 512746ccb7eSMatthias Ringwald 513aed68c56SMatthias Ringwald static void sdp_respond(void){ 514f20b4214SMatthias Ringwald if (!sdp_server_response_size ) return; 515f20b4214SMatthias Ringwald if (!sdp_server_l2cap_cid) return; 516746ccb7eSMatthias Ringwald 517746ccb7eSMatthias Ringwald // update state before sending packet (avoid getting called when new l2cap credit gets emitted) 518f20b4214SMatthias Ringwald uint16_t size = sdp_server_response_size; 519f20b4214SMatthias Ringwald sdp_server_response_size = 0; 520f20b4214SMatthias Ringwald l2cap_send(sdp_server_l2cap_cid, sdp_response_buffer, size); 521746ccb7eSMatthias Ringwald } 522746ccb7eSMatthias Ringwald 5237616f654SMatthias Ringwald // @pre space in list 5247616f654SMatthias Ringwald static void sdp_waiting_list_add(uint16_t cid){ 525f20b4214SMatthias Ringwald sdp_server_l2cap_waiting_list_cids[sdp_server_l2cap_waiting_list_count++] = cid; 5267616f654SMatthias Ringwald } 5277616f654SMatthias Ringwald 5287616f654SMatthias Ringwald // @pre at least one item in list 5297616f654SMatthias Ringwald static uint16_t sdp_waiting_list_get(void){ 530f20b4214SMatthias Ringwald uint16_t cid = sdp_server_l2cap_waiting_list_cids[0]; 531f20b4214SMatthias Ringwald sdp_server_l2cap_waiting_list_count--; 532f20b4214SMatthias Ringwald if (sdp_server_l2cap_waiting_list_count){ 533f20b4214SMatthias Ringwald memmove(&sdp_server_l2cap_waiting_list_cids[0], &sdp_server_l2cap_waiting_list_cids[1], sdp_server_l2cap_waiting_list_count * sizeof(uint16_t)); 5347616f654SMatthias Ringwald } 5357616f654SMatthias Ringwald return cid; 5367616f654SMatthias Ringwald } 5377616f654SMatthias Ringwald 538746ccb7eSMatthias Ringwald // we assume that we don't get two requests in a row 539746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 540746ccb7eSMatthias Ringwald uint16_t transaction_id; 541f20b4214SMatthias Ringwald sdp_pdu_id_t pdu_id; 542746ccb7eSMatthias Ringwald uint16_t remote_mtu; 5434443af49SMatthias Ringwald uint16_t param_len; 544746ccb7eSMatthias Ringwald 545746ccb7eSMatthias Ringwald switch (packet_type) { 546746ccb7eSMatthias Ringwald 547746ccb7eSMatthias Ringwald case L2CAP_DATA_PACKET: 548f20b4214SMatthias Ringwald pdu_id = (sdp_pdu_id_t) packet[0]; 549746ccb7eSMatthias Ringwald transaction_id = big_endian_read_16(packet, 1); 5504443af49SMatthias Ringwald param_len = big_endian_read_16(packet, 3); 551746ccb7eSMatthias Ringwald remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel); 552746ccb7eSMatthias Ringwald // account for our buffer 553746ccb7eSMatthias Ringwald if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){ 554746ccb7eSMatthias Ringwald remote_mtu = SDP_RESPONSE_BUFFER_SIZE; 555746ccb7eSMatthias Ringwald } 5564443af49SMatthias Ringwald // validate parm_len against packet size 557c1ab6cc1SMatthias Ringwald if ((param_len + 5) > size) { 5584443af49SMatthias Ringwald // just clear pdu_id 5594443af49SMatthias Ringwald pdu_id = SDP_ErrorResponse; 5604443af49SMatthias Ringwald } 561746ccb7eSMatthias Ringwald 562746ccb7eSMatthias Ringwald // log_info("SDP Request: type %u, transaction id %u, len %u, mtu %u", pdu_id, transaction_id, param_len, remote_mtu); 563746ccb7eSMatthias Ringwald switch (pdu_id){ 564746ccb7eSMatthias Ringwald 565746ccb7eSMatthias Ringwald case SDP_ServiceSearchRequest: 566f20b4214SMatthias Ringwald sdp_server_response_size = sdp_handle_service_search_request(packet, remote_mtu); 567746ccb7eSMatthias Ringwald break; 568746ccb7eSMatthias Ringwald 569746ccb7eSMatthias Ringwald case SDP_ServiceAttributeRequest: 570f20b4214SMatthias Ringwald sdp_server_response_size = sdp_handle_service_attribute_request(packet, remote_mtu); 571746ccb7eSMatthias Ringwald break; 572746ccb7eSMatthias Ringwald 573746ccb7eSMatthias Ringwald case SDP_ServiceSearchAttributeRequest: 574f20b4214SMatthias Ringwald sdp_server_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu); 575746ccb7eSMatthias Ringwald break; 576746ccb7eSMatthias Ringwald 577746ccb7eSMatthias Ringwald default: 578db96503fSMatthias Ringwald sdp_server_response_size = sdp_create_error_response(transaction_id, 0x0004); // invalid PDU size 579746ccb7eSMatthias Ringwald break; 580746ccb7eSMatthias Ringwald } 581f20b4214SMatthias Ringwald if (!sdp_server_response_size) break; 582f20b4214SMatthias Ringwald l2cap_request_can_send_now_event(sdp_server_l2cap_cid); 583746ccb7eSMatthias Ringwald break; 584746ccb7eSMatthias Ringwald 585746ccb7eSMatthias Ringwald case HCI_EVENT_PACKET: 586746ccb7eSMatthias Ringwald 5870e2df43fSMatthias Ringwald switch (hci_event_packet_get_type(packet)) { 588746ccb7eSMatthias Ringwald 589746ccb7eSMatthias Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 590f20b4214SMatthias Ringwald if (sdp_server_l2cap_cid) { 5917616f654SMatthias Ringwald // try to queue up 592f20b4214SMatthias Ringwald if (sdp_server_l2cap_waiting_list_count < SDP_WAITING_LIST_MAX_COUNT){ 5937616f654SMatthias Ringwald sdp_waiting_list_add(channel); 594f20b4214SMatthias Ringwald log_info("busy, queing incoming cid 0x%04x, now %u waiting", channel, sdp_server_l2cap_waiting_list_count); 5957616f654SMatthias Ringwald break; 5967616f654SMatthias Ringwald } 5977616f654SMatthias Ringwald 598746ccb7eSMatthias Ringwald // CONNECTION REJECTED DUE TO LIMITED RESOURCES 5997ef6a7bbSMatthias Ringwald l2cap_decline_connection(channel); 600746ccb7eSMatthias Ringwald break; 601746ccb7eSMatthias Ringwald } 602746ccb7eSMatthias Ringwald // accept 603f20b4214SMatthias Ringwald sdp_server_l2cap_cid = channel; 604f20b4214SMatthias Ringwald sdp_server_response_size = 0; 605f20b4214SMatthias Ringwald l2cap_accept_connection(sdp_server_l2cap_cid); 606746ccb7eSMatthias Ringwald break; 607746ccb7eSMatthias Ringwald 608746ccb7eSMatthias Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 609746ccb7eSMatthias Ringwald if (packet[2]) { 610746ccb7eSMatthias Ringwald // open failed -> reset 611f20b4214SMatthias Ringwald sdp_server_l2cap_cid = 0; 612746ccb7eSMatthias Ringwald } 613746ccb7eSMatthias Ringwald break; 614746ccb7eSMatthias Ringwald 615746ccb7eSMatthias Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 616aed68c56SMatthias Ringwald sdp_respond(); 617746ccb7eSMatthias Ringwald break; 618746ccb7eSMatthias Ringwald 619746ccb7eSMatthias Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 620f20b4214SMatthias Ringwald if (channel == sdp_server_l2cap_cid){ 621746ccb7eSMatthias Ringwald // reset 622f20b4214SMatthias Ringwald sdp_server_l2cap_cid = 0; 6237616f654SMatthias Ringwald 6247616f654SMatthias Ringwald // other request queued? 625f20b4214SMatthias Ringwald if (!sdp_server_l2cap_waiting_list_count) break; 6267616f654SMatthias Ringwald 6277616f654SMatthias Ringwald // get first item 628f20b4214SMatthias Ringwald sdp_server_l2cap_cid = sdp_waiting_list_get(); 6297616f654SMatthias Ringwald 630f20b4214SMatthias Ringwald log_info("disconnect, accept queued cid 0x%04x, now %u waiting", sdp_server_l2cap_cid, sdp_server_l2cap_waiting_list_count); 6317616f654SMatthias Ringwald 6327616f654SMatthias Ringwald // accept connection 633f20b4214SMatthias Ringwald sdp_server_response_size = 0; 634f20b4214SMatthias Ringwald l2cap_accept_connection(sdp_server_l2cap_cid); 635746ccb7eSMatthias Ringwald } 636746ccb7eSMatthias Ringwald break; 637746ccb7eSMatthias Ringwald 638746ccb7eSMatthias Ringwald default: 639746ccb7eSMatthias Ringwald // other event 640746ccb7eSMatthias Ringwald break; 641746ccb7eSMatthias Ringwald } 642746ccb7eSMatthias Ringwald break; 643746ccb7eSMatthias Ringwald 644746ccb7eSMatthias Ringwald default: 645746ccb7eSMatthias Ringwald // other packet type 646746ccb7eSMatthias Ringwald break; 647746ccb7eSMatthias Ringwald } 648746ccb7eSMatthias Ringwald } 649746ccb7eSMatthias Ringwald 65022d58ff8SMatthias Ringwald #ifdef ENABLE_TESTING_SUPPORT 65122d58ff8SMatthias Ringwald void sdp_server_set_single_record_response(bool enable){ 65222d58ff8SMatthias Ringwald sdp_server_testing_single_record_reponse = enable; 65322d58ff8SMatthias Ringwald } 65422d58ff8SMatthias Ringwald #endif