1*746ccb7eSMatthias Ringwald /* 2*746ccb7eSMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3*746ccb7eSMatthias Ringwald * 4*746ccb7eSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5*746ccb7eSMatthias Ringwald * modification, are permitted provided that the following conditions 6*746ccb7eSMatthias Ringwald * are met: 7*746ccb7eSMatthias Ringwald * 8*746ccb7eSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9*746ccb7eSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10*746ccb7eSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11*746ccb7eSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12*746ccb7eSMatthias Ringwald * documentation and/or other materials provided with the distribution. 13*746ccb7eSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14*746ccb7eSMatthias Ringwald * contributors may be used to endorse or promote products derived 15*746ccb7eSMatthias Ringwald * from this software without specific prior written permission. 16*746ccb7eSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17*746ccb7eSMatthias Ringwald * personal benefit and not for any commercial purpose or for 18*746ccb7eSMatthias Ringwald * monetary gain. 19*746ccb7eSMatthias Ringwald * 20*746ccb7eSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21*746ccb7eSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*746ccb7eSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23*746ccb7eSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24*746ccb7eSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25*746ccb7eSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26*746ccb7eSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27*746ccb7eSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28*746ccb7eSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29*746ccb7eSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30*746ccb7eSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31*746ccb7eSMatthias Ringwald * SUCH DAMAGE. 32*746ccb7eSMatthias Ringwald * 33*746ccb7eSMatthias Ringwald * Please inquire about commercial licensing options at 34*746ccb7eSMatthias Ringwald * [email protected] 35*746ccb7eSMatthias Ringwald * 36*746ccb7eSMatthias Ringwald */ 37*746ccb7eSMatthias Ringwald 38*746ccb7eSMatthias Ringwald /* 39*746ccb7eSMatthias Ringwald * Implementation of the Service Discovery Protocol Server 40*746ccb7eSMatthias Ringwald */ 41*746ccb7eSMatthias Ringwald 42*746ccb7eSMatthias Ringwald #include <stdio.h> 43*746ccb7eSMatthias Ringwald #include <string.h> 44*746ccb7eSMatthias Ringwald 45*746ccb7eSMatthias Ringwald #include "btstack_memory.h" 46*746ccb7eSMatthias Ringwald #include "btstack_debug.h" 47*746ccb7eSMatthias Ringwald #include "hci_dump.h" 48*746ccb7eSMatthias Ringwald #include "l2cap.h" 49*746ccb7eSMatthias Ringwald 50*746ccb7eSMatthias Ringwald #include "classic/sdp_server.h" 51*746ccb7eSMatthias Ringwald #include "classic/sdp_util.h" 52*746ccb7eSMatthias Ringwald 53*746ccb7eSMatthias Ringwald // max reserved ServiceRecordHandle 54*746ccb7eSMatthias Ringwald #define maxReservedServiceRecordHandle 0xffff 55*746ccb7eSMatthias Ringwald 56*746ccb7eSMatthias Ringwald // max SDP response matches L2CAP PDU -- allow to use smaller buffer 57*746ccb7eSMatthias Ringwald #ifndef SDP_RESPONSE_BUFFER_SIZE 58*746ccb7eSMatthias Ringwald #define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE) 59*746ccb7eSMatthias Ringwald #endif 60*746ccb7eSMatthias Ringwald 61*746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 62*746ccb7eSMatthias Ringwald 63*746ccb7eSMatthias Ringwald // registered service records 64*746ccb7eSMatthias Ringwald static btstack_linked_list_t sdp_service_records = NULL; 65*746ccb7eSMatthias Ringwald 66*746ccb7eSMatthias Ringwald // our handles start after the reserved range 67*746ccb7eSMatthias Ringwald static uint32_t sdp_next_service_record_handle = ((uint32_t) maxReservedServiceRecordHandle) + 2; 68*746ccb7eSMatthias Ringwald 69*746ccb7eSMatthias Ringwald static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE]; 70*746ccb7eSMatthias Ringwald 71*746ccb7eSMatthias Ringwald static uint16_t l2cap_cid = 0; 72*746ccb7eSMatthias Ringwald static uint16_t sdp_response_size = 0; 73*746ccb7eSMatthias Ringwald 74*746ccb7eSMatthias Ringwald void sdp_init(void){ 75*746ccb7eSMatthias Ringwald // register with l2cap psm sevices - max MTU 76*746ccb7eSMatthias Ringwald l2cap_register_service(sdp_packet_handler, PSM_SDP, 0xffff, LEVEL_0); 77*746ccb7eSMatthias Ringwald } 78*746ccb7eSMatthias Ringwald 79*746ccb7eSMatthias Ringwald uint32_t sdp_get_service_record_handle(const uint8_t * record){ 80*746ccb7eSMatthias Ringwald // TODO: make sdp_get_attribute_value_for_attribute_id accept const data to remove cast 81*746ccb7eSMatthias Ringwald uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id((uint8_t *)record, SDP_ServiceRecordHandle); 82*746ccb7eSMatthias Ringwald if (!serviceRecordHandleAttribute) return 0; 83*746ccb7eSMatthias Ringwald if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0; 84*746ccb7eSMatthias Ringwald if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0; 85*746ccb7eSMatthias Ringwald return bit_endian_read_32(serviceRecordHandleAttribute, 1); 86*746ccb7eSMatthias Ringwald } 87*746ccb7eSMatthias Ringwald 88*746ccb7eSMatthias Ringwald static service_record_item_t * sdp_get_record_item_for_handle(uint32_t handle){ 89*746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 90*746ccb7eSMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){ 91*746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 92*746ccb7eSMatthias Ringwald if (item->service_record_handle == handle){ 93*746ccb7eSMatthias Ringwald return item; 94*746ccb7eSMatthias Ringwald } 95*746ccb7eSMatthias Ringwald } 96*746ccb7eSMatthias Ringwald return NULL; 97*746ccb7eSMatthias Ringwald } 98*746ccb7eSMatthias Ringwald 99*746ccb7eSMatthias Ringwald uint8_t * sdp_get_record_for_handle(uint32_t handle){ 100*746ccb7eSMatthias Ringwald service_record_item_t * record_item = sdp_get_record_item_for_handle(handle); 101*746ccb7eSMatthias Ringwald if (!record_item) return 0; 102*746ccb7eSMatthias Ringwald return record_item->service_record; 103*746ccb7eSMatthias Ringwald } 104*746ccb7eSMatthias Ringwald 105*746ccb7eSMatthias Ringwald // get next free, unregistered service record handle 106*746ccb7eSMatthias Ringwald uint32_t sdp_create_service_record_handle(void){ 107*746ccb7eSMatthias Ringwald uint32_t handle = 0; 108*746ccb7eSMatthias Ringwald do { 109*746ccb7eSMatthias Ringwald handle = sdp_next_service_record_handle++; 110*746ccb7eSMatthias Ringwald if (sdp_get_record_item_for_handle(handle)) handle = 0; 111*746ccb7eSMatthias Ringwald } while (handle == 0); 112*746ccb7eSMatthias Ringwald return handle; 113*746ccb7eSMatthias Ringwald } 114*746ccb7eSMatthias Ringwald 115*746ccb7eSMatthias Ringwald /** 116*746ccb7eSMatthias Ringwald * @brief Register Service Record with database using ServiceRecordHandle stored in record 117*746ccb7eSMatthias Ringwald * @pre AttributeIDs are in ascending order 118*746ccb7eSMatthias Ringwald * @pre ServiceRecordHandle is first attribute and valid 119*746ccb7eSMatthias Ringwald * @param record is not copied! 120*746ccb7eSMatthias Ringwald * @result status 121*746ccb7eSMatthias Ringwald */ 122*746ccb7eSMatthias Ringwald uint8_t sdp_register_service(const uint8_t * record){ 123*746ccb7eSMatthias Ringwald 124*746ccb7eSMatthias Ringwald // validate service record handle. it must: exist, be in valid range, not have been already used 125*746ccb7eSMatthias Ringwald uint32_t record_handle = sdp_get_service_record_handle(record); 126*746ccb7eSMatthias Ringwald if (!record_handle) return SDP_HANDLE_INVALID; 127*746ccb7eSMatthias Ringwald if (record_handle <= maxReservedServiceRecordHandle) return SDP_HANDLE_INVALID; 128*746ccb7eSMatthias Ringwald if (sdp_get_record_item_for_handle(record_handle)) return SDP_HANDLE_ALREADY_REGISTERED; 129*746ccb7eSMatthias Ringwald 130*746ccb7eSMatthias Ringwald // alloc memory for new service_record_item 131*746ccb7eSMatthias Ringwald service_record_item_t * newRecordItem = btstack_memory_service_record_item_get(); 132*746ccb7eSMatthias Ringwald if (!newRecordItem) return BTSTACK_MEMORY_ALLOC_FAILED; 133*746ccb7eSMatthias Ringwald 134*746ccb7eSMatthias Ringwald // set handle and record 135*746ccb7eSMatthias Ringwald newRecordItem->service_record_handle = record_handle; 136*746ccb7eSMatthias Ringwald newRecordItem->service_record = (uint8_t*) record; 137*746ccb7eSMatthias Ringwald 138*746ccb7eSMatthias Ringwald // add to linked list 139*746ccb7eSMatthias Ringwald btstack_linked_list_add(&sdp_service_records, (btstack_linked_item_t *) newRecordItem); 140*746ccb7eSMatthias Ringwald 141*746ccb7eSMatthias Ringwald return 0; 142*746ccb7eSMatthias Ringwald } 143*746ccb7eSMatthias Ringwald 144*746ccb7eSMatthias Ringwald // 145*746ccb7eSMatthias Ringwald // unregister service record 146*746ccb7eSMatthias Ringwald // 147*746ccb7eSMatthias Ringwald void sdp_unregister_service(uint32_t service_record_handle){ 148*746ccb7eSMatthias Ringwald service_record_item_t * record_item = sdp_get_record_item_for_handle(service_record_handle); 149*746ccb7eSMatthias Ringwald if (!record_item) return; 150*746ccb7eSMatthias Ringwald btstack_linked_list_remove(&sdp_service_records, (btstack_linked_item_t *) record_item); 151*746ccb7eSMatthias Ringwald } 152*746ccb7eSMatthias Ringwald 153*746ccb7eSMatthias Ringwald // PDU 154*746ccb7eSMatthias Ringwald // PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, .. 155*746ccb7eSMatthias Ringwald 156*746ccb7eSMatthias Ringwald static int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){ 157*746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ErrorResponse; 158*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 159*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, 2); 160*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, error_code); // invalid syntax 161*746ccb7eSMatthias Ringwald return 7; 162*746ccb7eSMatthias Ringwald } 163*746ccb7eSMatthias Ringwald 164*746ccb7eSMatthias Ringwald int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){ 165*746ccb7eSMatthias Ringwald 166*746ccb7eSMatthias Ringwald // get request details 167*746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 168*746ccb7eSMatthias Ringwald // not used yet - uint16_t param_len = big_endian_read_16(packet, 3); 169*746ccb7eSMatthias Ringwald uint8_t * serviceSearchPattern = &packet[5]; 170*746ccb7eSMatthias Ringwald uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); 171*746ccb7eSMatthias Ringwald uint16_t maximumServiceRecordCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen); 172*746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2]; 173*746ccb7eSMatthias Ringwald 174*746ccb7eSMatthias Ringwald // calc maxumumServiceRecordCount based on remote MTU 175*746ccb7eSMatthias Ringwald uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4; 176*746ccb7eSMatthias Ringwald 177*746ccb7eSMatthias Ringwald // continuation state contains index of next service record to examine 178*746ccb7eSMatthias Ringwald int continuation = 0; 179*746ccb7eSMatthias Ringwald uint16_t continuation_index = 0; 180*746ccb7eSMatthias Ringwald if (continuationState[0] == 2){ 181*746ccb7eSMatthias Ringwald continuation_index = big_endian_read_16(continuationState, 1); 182*746ccb7eSMatthias Ringwald } 183*746ccb7eSMatthias Ringwald 184*746ccb7eSMatthias Ringwald // get and limit total count 185*746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 186*746ccb7eSMatthias Ringwald uint16_t total_service_count = 0; 187*746ccb7eSMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){ 188*746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 189*746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 190*746ccb7eSMatthias Ringwald total_service_count++; 191*746ccb7eSMatthias Ringwald } 192*746ccb7eSMatthias Ringwald if (total_service_count > maximumServiceRecordCount){ 193*746ccb7eSMatthias Ringwald total_service_count = maximumServiceRecordCount; 194*746ccb7eSMatthias Ringwald } 195*746ccb7eSMatthias Ringwald 196*746ccb7eSMatthias Ringwald // ServiceRecordHandleList at 9 197*746ccb7eSMatthias Ringwald uint16_t pos = 9; 198*746ccb7eSMatthias Ringwald uint16_t current_service_count = 0; 199*746ccb7eSMatthias Ringwald uint16_t current_service_index = 0; 200*746ccb7eSMatthias Ringwald uint16_t matching_service_count = 0; 201*746ccb7eSMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){ 202*746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 203*746ccb7eSMatthias Ringwald 204*746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 205*746ccb7eSMatthias Ringwald matching_service_count++; 206*746ccb7eSMatthias Ringwald 207*746ccb7eSMatthias Ringwald if (current_service_index < continuation_index) continue; 208*746ccb7eSMatthias Ringwald 209*746ccb7eSMatthias Ringwald big_endian_store_32(sdp_response_buffer, pos, item->service_record_handle); 210*746ccb7eSMatthias Ringwald pos += 4; 211*746ccb7eSMatthias Ringwald current_service_count++; 212*746ccb7eSMatthias Ringwald 213*746ccb7eSMatthias Ringwald if (matching_service_count >= total_service_count) break; 214*746ccb7eSMatthias Ringwald 215*746ccb7eSMatthias Ringwald if (current_service_count >= maxNrServiceRecordsPerResponse){ 216*746ccb7eSMatthias Ringwald continuation = 1; 217*746ccb7eSMatthias Ringwald continuation_index = current_service_index + 1; 218*746ccb7eSMatthias Ringwald break; 219*746ccb7eSMatthias Ringwald } 220*746ccb7eSMatthias Ringwald } 221*746ccb7eSMatthias Ringwald 222*746ccb7eSMatthias Ringwald // Store continuation state 223*746ccb7eSMatthias Ringwald if (continuation) { 224*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 2; 225*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_index); 226*746ccb7eSMatthias Ringwald pos += 2; 227*746ccb7eSMatthias Ringwald } else { 228*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 229*746ccb7eSMatthias Ringwald } 230*746ccb7eSMatthias Ringwald 231*746ccb7eSMatthias Ringwald // header 232*746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceSearchResponse; 233*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 234*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 235*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, total_service_count); 236*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 7, current_service_count); 237*746ccb7eSMatthias Ringwald 238*746ccb7eSMatthias Ringwald return pos; 239*746ccb7eSMatthias Ringwald } 240*746ccb7eSMatthias Ringwald 241*746ccb7eSMatthias Ringwald int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){ 242*746ccb7eSMatthias Ringwald 243*746ccb7eSMatthias Ringwald // get request details 244*746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 245*746ccb7eSMatthias Ringwald // not used yet - uint16_t param_len = big_endian_read_16(packet, 3); 246*746ccb7eSMatthias Ringwald uint32_t serviceRecordHandle = bit_endian_read_32(packet, 5); 247*746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount = big_endian_read_16(packet, 9); 248*746ccb7eSMatthias Ringwald uint8_t * attributeIDList = &packet[11]; 249*746ccb7eSMatthias Ringwald uint16_t attributeIDListLen = de_get_len(attributeIDList); 250*746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[11+attributeIDListLen]; 251*746ccb7eSMatthias Ringwald 252*746ccb7eSMatthias Ringwald // calc maximumAttributeByteCount based on remote MTU 253*746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3); 254*746ccb7eSMatthias Ringwald if (maximumAttributeByteCount2 < maximumAttributeByteCount) { 255*746ccb7eSMatthias Ringwald maximumAttributeByteCount = maximumAttributeByteCount2; 256*746ccb7eSMatthias Ringwald } 257*746ccb7eSMatthias Ringwald 258*746ccb7eSMatthias Ringwald // continuation state contains the offset into the complete response 259*746ccb7eSMatthias Ringwald uint16_t continuation_offset = 0; 260*746ccb7eSMatthias Ringwald if (continuationState[0] == 2){ 261*746ccb7eSMatthias Ringwald continuation_offset = big_endian_read_16(continuationState, 1); 262*746ccb7eSMatthias Ringwald } 263*746ccb7eSMatthias Ringwald 264*746ccb7eSMatthias Ringwald // get service record 265*746ccb7eSMatthias Ringwald service_record_item_t * item = sdp_get_record_item_for_handle(serviceRecordHandle); 266*746ccb7eSMatthias Ringwald if (!item){ 267*746ccb7eSMatthias Ringwald // service record handle doesn't exist 268*746ccb7eSMatthias Ringwald return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle 269*746ccb7eSMatthias Ringwald } 270*746ccb7eSMatthias Ringwald 271*746ccb7eSMatthias Ringwald 272*746ccb7eSMatthias Ringwald // AttributeList - starts at offset 7 273*746ccb7eSMatthias Ringwald uint16_t pos = 7; 274*746ccb7eSMatthias Ringwald 275*746ccb7eSMatthias Ringwald if (continuation_offset == 0){ 276*746ccb7eSMatthias Ringwald 277*746ccb7eSMatthias Ringwald // get size of this record 278*746ccb7eSMatthias Ringwald uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); 279*746ccb7eSMatthias Ringwald 280*746ccb7eSMatthias Ringwald // store DES 281*746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); 282*746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 283*746ccb7eSMatthias Ringwald pos += 3; 284*746ccb7eSMatthias Ringwald } 285*746ccb7eSMatthias Ringwald 286*746ccb7eSMatthias Ringwald // copy maximumAttributeByteCount from record 287*746ccb7eSMatthias Ringwald uint16_t bytes_used; 288*746ccb7eSMatthias Ringwald int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); 289*746ccb7eSMatthias Ringwald pos += bytes_used; 290*746ccb7eSMatthias Ringwald 291*746ccb7eSMatthias Ringwald uint16_t attributeListByteCount = pos - 7; 292*746ccb7eSMatthias Ringwald 293*746ccb7eSMatthias Ringwald if (complete) { 294*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 295*746ccb7eSMatthias Ringwald } else { 296*746ccb7eSMatthias Ringwald continuation_offset += bytes_used; 297*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 2; 298*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_offset); 299*746ccb7eSMatthias Ringwald pos += 2; 300*746ccb7eSMatthias Ringwald } 301*746ccb7eSMatthias Ringwald 302*746ccb7eSMatthias Ringwald // header 303*746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceAttributeResponse; 304*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 305*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 306*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, attributeListByteCount); 307*746ccb7eSMatthias Ringwald 308*746ccb7eSMatthias Ringwald return pos; 309*746ccb7eSMatthias Ringwald } 310*746ccb7eSMatthias Ringwald 311*746ccb7eSMatthias Ringwald static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){ 312*746ccb7eSMatthias Ringwald uint16_t total_response_size = 0; 313*746ccb7eSMatthias Ringwald btstack_linked_item_t *it; 314*746ccb7eSMatthias Ringwald for (it = (btstack_linked_item_t *) sdp_service_records; it ; it = it->next){ 315*746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 316*746ccb7eSMatthias Ringwald 317*746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 318*746ccb7eSMatthias Ringwald 319*746ccb7eSMatthias Ringwald // for all service records that match 320*746ccb7eSMatthias Ringwald total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList); 321*746ccb7eSMatthias Ringwald } 322*746ccb7eSMatthias Ringwald return total_response_size; 323*746ccb7eSMatthias Ringwald } 324*746ccb7eSMatthias Ringwald 325*746ccb7eSMatthias Ringwald int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){ 326*746ccb7eSMatthias Ringwald 327*746ccb7eSMatthias Ringwald // SDP header before attribute sevice list: 7 328*746ccb7eSMatthias Ringwald // Continuation, worst case: 5 329*746ccb7eSMatthias Ringwald 330*746ccb7eSMatthias Ringwald // get request details 331*746ccb7eSMatthias Ringwald uint16_t transaction_id = big_endian_read_16(packet, 1); 332*746ccb7eSMatthias Ringwald // not used yet - uint16_t param_len = big_endian_read_16(packet, 3); 333*746ccb7eSMatthias Ringwald uint8_t * serviceSearchPattern = &packet[5]; 334*746ccb7eSMatthias Ringwald uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); 335*746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount = big_endian_read_16(packet, 5 + serviceSearchPatternLen); 336*746ccb7eSMatthias Ringwald uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2]; 337*746ccb7eSMatthias Ringwald uint16_t attributeIDListLen = de_get_len(attributeIDList); 338*746ccb7eSMatthias Ringwald uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen]; 339*746ccb7eSMatthias Ringwald 340*746ccb7eSMatthias Ringwald // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block 341*746ccb7eSMatthias Ringwald uint16_t maximumAttributeByteCount2 = remote_mtu - 12; 342*746ccb7eSMatthias Ringwald if (maximumAttributeByteCount2 < maximumAttributeByteCount) { 343*746ccb7eSMatthias Ringwald maximumAttributeByteCount = maximumAttributeByteCount2; 344*746ccb7eSMatthias Ringwald } 345*746ccb7eSMatthias Ringwald 346*746ccb7eSMatthias Ringwald // continuation state contains: index of next service record to examine 347*746ccb7eSMatthias Ringwald // continuation state contains: byte offset into this service record 348*746ccb7eSMatthias Ringwald uint16_t continuation_service_index = 0; 349*746ccb7eSMatthias Ringwald uint16_t continuation_offset = 0; 350*746ccb7eSMatthias Ringwald if (continuationState[0] == 4){ 351*746ccb7eSMatthias Ringwald continuation_service_index = big_endian_read_16(continuationState, 1); 352*746ccb7eSMatthias Ringwald continuation_offset = big_endian_read_16(continuationState, 3); 353*746ccb7eSMatthias Ringwald } 354*746ccb7eSMatthias Ringwald 355*746ccb7eSMatthias Ringwald // log_info("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u", continuation_service_index, continuation_offset, maximumAttributeByteCount); 356*746ccb7eSMatthias Ringwald 357*746ccb7eSMatthias Ringwald // AttributeLists - starts at offset 7 358*746ccb7eSMatthias Ringwald uint16_t pos = 7; 359*746ccb7eSMatthias Ringwald 360*746ccb7eSMatthias Ringwald // add DES with total size for first request 361*746ccb7eSMatthias Ringwald if (continuation_service_index == 0 && continuation_offset == 0){ 362*746ccb7eSMatthias Ringwald uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList); 363*746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size); 364*746ccb7eSMatthias Ringwald // log_info("total response size %u", total_response_size); 365*746ccb7eSMatthias Ringwald pos += 3; 366*746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 367*746ccb7eSMatthias Ringwald } 368*746ccb7eSMatthias Ringwald 369*746ccb7eSMatthias Ringwald // create attribute list 370*746ccb7eSMatthias Ringwald int first_answer = 1; 371*746ccb7eSMatthias Ringwald int continuation = 0; 372*746ccb7eSMatthias Ringwald uint16_t current_service_index = 0; 373*746ccb7eSMatthias Ringwald btstack_linked_item_t *it = (btstack_linked_item_t *) sdp_service_records; 374*746ccb7eSMatthias Ringwald for ( ; it ; it = it->next, ++current_service_index){ 375*746ccb7eSMatthias Ringwald service_record_item_t * item = (service_record_item_t *) it; 376*746ccb7eSMatthias Ringwald 377*746ccb7eSMatthias Ringwald if (current_service_index < continuation_service_index ) continue; 378*746ccb7eSMatthias Ringwald if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; 379*746ccb7eSMatthias Ringwald 380*746ccb7eSMatthias Ringwald if (continuation_offset == 0){ 381*746ccb7eSMatthias Ringwald 382*746ccb7eSMatthias Ringwald // get size of this record 383*746ccb7eSMatthias Ringwald uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); 384*746ccb7eSMatthias Ringwald 385*746ccb7eSMatthias Ringwald // stop if complete record doesn't fits into response but we already have a partial response 386*746ccb7eSMatthias Ringwald if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) { 387*746ccb7eSMatthias Ringwald continuation = 1; 388*746ccb7eSMatthias Ringwald break; 389*746ccb7eSMatthias Ringwald } 390*746ccb7eSMatthias Ringwald 391*746ccb7eSMatthias Ringwald // store DES 392*746ccb7eSMatthias Ringwald de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); 393*746ccb7eSMatthias Ringwald pos += 3; 394*746ccb7eSMatthias Ringwald maximumAttributeByteCount -= 3; 395*746ccb7eSMatthias Ringwald } 396*746ccb7eSMatthias Ringwald 397*746ccb7eSMatthias Ringwald first_answer = 0; 398*746ccb7eSMatthias Ringwald 399*746ccb7eSMatthias Ringwald // copy maximumAttributeByteCount from record 400*746ccb7eSMatthias Ringwald uint16_t bytes_used; 401*746ccb7eSMatthias Ringwald int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); 402*746ccb7eSMatthias Ringwald pos += bytes_used; 403*746ccb7eSMatthias Ringwald maximumAttributeByteCount -= bytes_used; 404*746ccb7eSMatthias Ringwald 405*746ccb7eSMatthias Ringwald if (complete) { 406*746ccb7eSMatthias Ringwald continuation_offset = 0; 407*746ccb7eSMatthias Ringwald continue; 408*746ccb7eSMatthias Ringwald } 409*746ccb7eSMatthias Ringwald 410*746ccb7eSMatthias Ringwald continuation = 1; 411*746ccb7eSMatthias Ringwald continuation_offset += bytes_used; 412*746ccb7eSMatthias Ringwald break; 413*746ccb7eSMatthias Ringwald } 414*746ccb7eSMatthias Ringwald 415*746ccb7eSMatthias Ringwald uint16_t attributeListsByteCount = pos - 7; 416*746ccb7eSMatthias Ringwald 417*746ccb7eSMatthias Ringwald // Continuation State 418*746ccb7eSMatthias Ringwald if (continuation){ 419*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 4; 420*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index); 421*746ccb7eSMatthias Ringwald pos += 2; 422*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, pos, continuation_offset); 423*746ccb7eSMatthias Ringwald pos += 2; 424*746ccb7eSMatthias Ringwald } else { 425*746ccb7eSMatthias Ringwald // complete 426*746ccb7eSMatthias Ringwald sdp_response_buffer[pos++] = 0; 427*746ccb7eSMatthias Ringwald } 428*746ccb7eSMatthias Ringwald 429*746ccb7eSMatthias Ringwald // create SDP header 430*746ccb7eSMatthias Ringwald sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse; 431*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 1, transaction_id); 432*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload 433*746ccb7eSMatthias Ringwald big_endian_store_16(sdp_response_buffer, 5, attributeListsByteCount); 434*746ccb7eSMatthias Ringwald 435*746ccb7eSMatthias Ringwald return pos; 436*746ccb7eSMatthias Ringwald } 437*746ccb7eSMatthias Ringwald 438*746ccb7eSMatthias Ringwald static void sdp_try_respond(void){ 439*746ccb7eSMatthias Ringwald if (!sdp_response_size ) return; 440*746ccb7eSMatthias Ringwald if (!l2cap_cid) return; 441*746ccb7eSMatthias Ringwald if (!l2cap_can_send_packet_now(l2cap_cid)) return; 442*746ccb7eSMatthias Ringwald 443*746ccb7eSMatthias Ringwald // update state before sending packet (avoid getting called when new l2cap credit gets emitted) 444*746ccb7eSMatthias Ringwald uint16_t size = sdp_response_size; 445*746ccb7eSMatthias Ringwald sdp_response_size = 0; 446*746ccb7eSMatthias Ringwald l2cap_send(l2cap_cid, sdp_response_buffer, size); 447*746ccb7eSMatthias Ringwald } 448*746ccb7eSMatthias Ringwald 449*746ccb7eSMatthias Ringwald // we assume that we don't get two requests in a row 450*746ccb7eSMatthias Ringwald static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 451*746ccb7eSMatthias Ringwald uint16_t transaction_id; 452*746ccb7eSMatthias Ringwald SDP_PDU_ID_t pdu_id; 453*746ccb7eSMatthias Ringwald uint16_t remote_mtu; 454*746ccb7eSMatthias Ringwald // uint16_t param_len; 455*746ccb7eSMatthias Ringwald 456*746ccb7eSMatthias Ringwald switch (packet_type) { 457*746ccb7eSMatthias Ringwald 458*746ccb7eSMatthias Ringwald case L2CAP_DATA_PACKET: 459*746ccb7eSMatthias Ringwald pdu_id = (SDP_PDU_ID_t) packet[0]; 460*746ccb7eSMatthias Ringwald transaction_id = big_endian_read_16(packet, 1); 461*746ccb7eSMatthias Ringwald // param_len = big_endian_read_16(packet, 3); 462*746ccb7eSMatthias Ringwald remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel); 463*746ccb7eSMatthias Ringwald // account for our buffer 464*746ccb7eSMatthias Ringwald if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){ 465*746ccb7eSMatthias Ringwald remote_mtu = SDP_RESPONSE_BUFFER_SIZE; 466*746ccb7eSMatthias Ringwald } 467*746ccb7eSMatthias Ringwald 468*746ccb7eSMatthias Ringwald // log_info("SDP Request: type %u, transaction id %u, len %u, mtu %u", pdu_id, transaction_id, param_len, remote_mtu); 469*746ccb7eSMatthias Ringwald switch (pdu_id){ 470*746ccb7eSMatthias Ringwald 471*746ccb7eSMatthias Ringwald case SDP_ServiceSearchRequest: 472*746ccb7eSMatthias Ringwald sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu); 473*746ccb7eSMatthias Ringwald break; 474*746ccb7eSMatthias Ringwald 475*746ccb7eSMatthias Ringwald case SDP_ServiceAttributeRequest: 476*746ccb7eSMatthias Ringwald sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu); 477*746ccb7eSMatthias Ringwald break; 478*746ccb7eSMatthias Ringwald 479*746ccb7eSMatthias Ringwald case SDP_ServiceSearchAttributeRequest: 480*746ccb7eSMatthias Ringwald sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu); 481*746ccb7eSMatthias Ringwald break; 482*746ccb7eSMatthias Ringwald 483*746ccb7eSMatthias Ringwald default: 484*746ccb7eSMatthias Ringwald sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax 485*746ccb7eSMatthias Ringwald break; 486*746ccb7eSMatthias Ringwald } 487*746ccb7eSMatthias Ringwald 488*746ccb7eSMatthias Ringwald sdp_try_respond(); 489*746ccb7eSMatthias Ringwald 490*746ccb7eSMatthias Ringwald break; 491*746ccb7eSMatthias Ringwald 492*746ccb7eSMatthias Ringwald case HCI_EVENT_PACKET: 493*746ccb7eSMatthias Ringwald 494*746ccb7eSMatthias Ringwald switch (packet[0]) { 495*746ccb7eSMatthias Ringwald 496*746ccb7eSMatthias Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 497*746ccb7eSMatthias Ringwald if (l2cap_cid) { 498*746ccb7eSMatthias Ringwald // CONNECTION REJECTED DUE TO LIMITED RESOURCES 499*746ccb7eSMatthias Ringwald l2cap_decline_connection(channel, 0x04); 500*746ccb7eSMatthias Ringwald break; 501*746ccb7eSMatthias Ringwald } 502*746ccb7eSMatthias Ringwald // accept 503*746ccb7eSMatthias Ringwald l2cap_cid = channel; 504*746ccb7eSMatthias Ringwald sdp_response_size = 0; 505*746ccb7eSMatthias Ringwald l2cap_accept_connection(channel); 506*746ccb7eSMatthias Ringwald break; 507*746ccb7eSMatthias Ringwald 508*746ccb7eSMatthias Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 509*746ccb7eSMatthias Ringwald if (packet[2]) { 510*746ccb7eSMatthias Ringwald // open failed -> reset 511*746ccb7eSMatthias Ringwald l2cap_cid = 0; 512*746ccb7eSMatthias Ringwald } 513*746ccb7eSMatthias Ringwald break; 514*746ccb7eSMatthias Ringwald 515*746ccb7eSMatthias Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 516*746ccb7eSMatthias Ringwald sdp_try_respond(); 517*746ccb7eSMatthias Ringwald break; 518*746ccb7eSMatthias Ringwald 519*746ccb7eSMatthias Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 520*746ccb7eSMatthias Ringwald if (channel == l2cap_cid){ 521*746ccb7eSMatthias Ringwald // reset 522*746ccb7eSMatthias Ringwald l2cap_cid = 0; 523*746ccb7eSMatthias Ringwald } 524*746ccb7eSMatthias Ringwald break; 525*746ccb7eSMatthias Ringwald 526*746ccb7eSMatthias Ringwald default: 527*746ccb7eSMatthias Ringwald // other event 528*746ccb7eSMatthias Ringwald break; 529*746ccb7eSMatthias Ringwald } 530*746ccb7eSMatthias Ringwald break; 531*746ccb7eSMatthias Ringwald 532*746ccb7eSMatthias Ringwald default: 533*746ccb7eSMatthias Ringwald // other packet type 534*746ccb7eSMatthias Ringwald break; 535*746ccb7eSMatthias Ringwald } 536*746ccb7eSMatthias Ringwald } 537*746ccb7eSMatthias Ringwald 538