Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of BTstack by
Diff: BTstack/sdp.c
- Revision:
- 0:1ed23ab1345f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/sdp.c Tue Jun 26 14:27:45 2012 +0000
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 4. Any redistribution, use, or modification is done solely for
+ * personal benefit and not for any commercial purpose or for
+ * monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
+ * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ * Implementation of the Service Discovery Protocol Server
+ */
+
+#include "sdp.h"
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include <btstack/sdp_util.h>
+
+#include "hci_dump.h"
+#include "l2cap.h"
+
+#include "debug.h"
+
+// max reserved ServiceRecordHandle
+#define maxReservedServiceRecordHandle 0xffff
+
+// max SDP response
+#define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE)
+
+static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+
+// registered service records
+static linked_list_t sdp_service_records = NULL;
+
+// our handles start after the reserved range
+static uint32_t sdp_next_service_record_handle = maxReservedServiceRecordHandle + 2;
+
+static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE];
+
+static void (*app_packet_handler)(void * connection, uint8_t packet_type,
+ uint16_t channel, uint8_t *packet, uint16_t size) = NULL;
+
+static uint16_t l2cap_cid = 0;
+static uint16_t sdp_response_size = 0;
+
+void sdp_init(){
+ // register with l2cap psm sevices - max MTU
+ l2cap_register_service_internal(NULL, sdp_packet_handler, PSM_SDP, 0xffff);
+}
+
+// register packet handler
+void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
+ uint16_t channel, uint8_t *packet, uint16_t size)){
+ app_packet_handler = handler;
+ l2cap_cid = 0;
+}
+
+uint32_t sdp_get_service_record_handle(uint8_t * record){
+ uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
+ if (!serviceRecordHandleAttribute) return 0;
+ if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0;
+ if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0;
+ return READ_NET_32(serviceRecordHandleAttribute, 1);
+}
+
+// data: event(8), len(8), status(8), service_record_handle(32)
+static void sdp_emit_service_registered(void *connection, uint32_t handle, uint8_t status) {
+ if (!app_packet_handler) return;
+ uint8_t event[7];
+ event[0] = SDP_SERVICE_REGISTERED;
+ event[1] = sizeof(event) - 2;
+ event[2] = status;
+ bt_store_32(event, 3, handle);
+ hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event));
+ (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event));
+}
+
+service_record_item_t * sdp_get_record_for_handle(uint32_t handle){
+ linked_item_t *it;
+ for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
+ service_record_item_t * item = (service_record_item_t *) it;
+ if (item->service_record_handle == handle){
+ return item;
+ }
+ }
+ return NULL;
+}
+
+// get next free, unregistered service record handle
+uint32_t sdp_create_service_record_handle(void){
+ uint32_t handle = 0;
+ do {
+ handle = sdp_next_service_record_handle++;
+ if (sdp_get_record_for_handle(handle)) handle = 0;
+ } while (handle == 0);
+ return handle;
+}
+
+#ifdef EMBEDDED
+
+// register service record internally - this special version doesn't copy the record, it should not be freeed
+// pre: AttributeIDs are in ascending order
+// pre: ServiceRecordHandle is first attribute and valid
+// pre: record
+// @returns ServiceRecordHandle or 0 if registration failed
+uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item){
+ // get user record handle
+ uint32_t record_handle = record_item->service_record_handle;
+ // get actual record
+ uint8_t *record = record_item->service_record;
+
+ // check for ServiceRecordHandle attribute, returns pointer or null
+ uint8_t * req_record_handle = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle);
+ if (!req_record_handle) {
+ log_error("SDP Error - record does not contain ServiceRecordHandle attribute\n");
+ return 0;
+ }
+
+ // validate service record handle is not in reserved range
+ if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
+
+ // check if already in use
+ if (record_handle) {
+ if (sdp_get_record_for_handle(record_handle)) {
+ record_handle = 0;
+ }
+ }
+
+ // create new handle if needed
+ if (!record_handle){
+ record_handle = sdp_create_service_record_handle();
+ // Write the handle back into the record too
+ record_item->service_record_handle = record_handle;
+ sdp_set_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle, record_handle);
+ }
+
+ // add to linked list
+ linked_list_add(&sdp_service_records, (linked_item_t *) record_item);
+
+ sdp_emit_service_registered(connection, 0, record_item->service_record_handle);
+
+ return record_handle;
+}
+
+#else
+
+// AttributeIDList used to remove ServiceRecordHandle
+static const uint8_t removeServiceRecordHandleAttributeIDList[] = { 0x36, 0x00, 0x05, 0x0A, 0x00, 0x01, 0xFF, 0xFF };
+
+// register service record internally - the normal version creates a copy of the record
+// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
+// @returns ServiceRecordHandle or 0 if registration failed
+uint32_t sdp_register_service_internal(void *connection, uint8_t * record){
+
+ // dump for now
+ // printf("Register service record\n");
+ // de_dump_data_element(record);
+
+ // get user record handle
+ uint32_t record_handle = sdp_get_service_record_handle(record);
+
+ // validate service record handle is not in reserved range
+ if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0;
+
+ // check if already in use
+ if (record_handle) {
+ if (sdp_get_record_for_handle(record_handle)) {
+ record_handle = 0;
+ }
+ }
+
+ // create new handle if needed
+ if (!record_handle){
+ record_handle = sdp_create_service_record_handle();
+ }
+
+ // calculate size of new service record: DES (2 byte len)
+ // + ServiceRecordHandle attribute (UINT16 UINT32) + size of existing attributes
+ uint16_t recordSize = 3 + (3 + 5) + de_get_data_size(record);
+
+ // alloc memory for new service_record_item
+ service_record_item_t * newRecordItem = (service_record_item_t *) malloc(recordSize + sizeof(service_record_item_t));
+ if (!newRecordItem) {
+ sdp_emit_service_registered(connection, 0, BTSTACK_MEMORY_ALLOC_FAILED);
+ return 0;
+ }
+ // link new service item to client connection
+ newRecordItem->connection = connection;
+
+ // set new handle
+ newRecordItem->service_record_handle = record_handle;
+
+ // create updated service record
+ uint8_t * newRecord = (uint8_t *) &(newRecordItem->service_record);
+
+ // create DES for new record
+ de_create_sequence(newRecord);
+
+ // set service record handle
+ de_add_number(newRecord, DE_UINT, DE_SIZE_16, 0);
+ de_add_number(newRecord, DE_UINT, DE_SIZE_32, record_handle);
+
+ // add other attributes
+ sdp_append_attributes_in_attributeIDList(record, (uint8_t *) removeServiceRecordHandleAttributeIDList, 0, recordSize, newRecord);
+
+ // dump for now
+ // de_dump_data_element(newRecord);
+ // printf("reserved size %u, actual size %u\n", recordSize, de_get_len(newRecord));
+
+ // add to linked list
+ linked_list_add(&sdp_service_records, (linked_item_t *) newRecordItem);
+
+ sdp_emit_service_registered(connection, 0, newRecordItem->service_record_handle);
+
+ return record_handle;
+}
+
+#endif
+
+// unregister service record internally
+//
+// makes sure one client cannot remove service records of other clients
+//
+void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle){
+ service_record_item_t * record_item = sdp_get_record_for_handle(service_record_handle);
+ if (record_item && record_item->connection == connection) {
+ linked_list_remove(&sdp_service_records, (linked_item_t *) record_item);
+ }
+}
+
+// remove all service record for a client connection
+void sdp_unregister_services_for_connection(void *connection){
+ linked_item_t *it = (linked_item_t *) &sdp_service_records;
+ while (it->next){
+ service_record_item_t *record_item = (service_record_item_t *) it->next;
+ if (record_item->connection == connection){
+ it->next = it->next->next;
+#ifndef EMBEDDED
+ free(record_item);
+#endif
+ } else {
+ it = it->next;
+ }
+ }
+}
+
+// PDU
+// PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, ..
+
+int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){
+ sdp_response_buffer[0] = SDP_ErrorResponse;
+ net_store_16(sdp_response_buffer, 1, transaction_id);
+ net_store_16(sdp_response_buffer, 3, 2);
+ net_store_16(sdp_response_buffer, 5, error_code); // invalid syntax
+ return 7;
+}
+
+int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){
+
+ // get request details
+ uint16_t transaction_id = READ_NET_16(packet, 1);
+ // not used yet - uint16_t param_len = READ_NET_16(packet, 3);
+ uint8_t * serviceSearchPattern = &packet[5];
+ uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
+ uint16_t maximumServiceRecordCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
+ uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2];
+
+ // calc maxumumServiceRecordCount based on remote MTU
+ uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4;
+
+ // continuation state contains index of next service record to examine
+ int continuation = 0;
+ uint16_t continuation_index = 0;
+ if (continuationState[0] == 2){
+ continuation_index = READ_NET_16(continuationState, 1);
+ }
+
+ // get and limit total count
+ linked_item_t *it;
+ uint16_t total_service_count = 0;
+ for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
+ service_record_item_t * item = (service_record_item_t *) it;
+ if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
+ total_service_count++;
+ }
+ if (total_service_count > maximumServiceRecordCount){
+ total_service_count = maximumServiceRecordCount;
+ }
+
+ // ServiceRecordHandleList at 9
+ uint16_t pos = 9;
+ uint16_t current_service_count = 0;
+ uint16_t current_service_index = 0;
+ uint16_t matching_service_count = 0;
+ for (it = (linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){
+ service_record_item_t * item = (service_record_item_t *) it;
+
+ if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
+ matching_service_count++;
+
+ if (current_service_index < continuation_index) continue;
+
+ net_store_32(sdp_response_buffer, pos, item->service_record_handle);
+ pos += 4;
+ current_service_count++;
+
+ if (matching_service_count >= total_service_count) break;
+
+ if (current_service_count >= maxNrServiceRecordsPerResponse){
+ continuation = 1;
+ continuation_index = current_service_index + 1;
+ break;
+ }
+ }
+
+ // Store continuation state
+ if (continuation) {
+ sdp_response_buffer[pos++] = 2;
+ net_store_16(sdp_response_buffer, pos, continuation_index);
+ pos += 2;
+ } else {
+ sdp_response_buffer[pos++] = 0;
+ }
+
+ // header
+ sdp_response_buffer[0] = SDP_ServiceSearchResponse;
+ net_store_16(sdp_response_buffer, 1, transaction_id);
+ net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
+ net_store_16(sdp_response_buffer, 5, total_service_count);
+ net_store_16(sdp_response_buffer, 7, current_service_count);
+
+ return pos;
+}
+
+int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){
+
+ // get request details
+ uint16_t transaction_id = READ_NET_16(packet, 1);
+ // not used yet - uint16_t param_len = READ_NET_16(packet, 3);
+ uint32_t serviceRecordHandle = READ_NET_32(packet, 5);
+ uint16_t maximumAttributeByteCount = READ_NET_16(packet, 9);
+ uint8_t * attributeIDList = &packet[11];
+ uint16_t attributeIDListLen = de_get_len(attributeIDList);
+ uint8_t * continuationState = &packet[11+attributeIDListLen];
+
+ // calc maximumAttributeByteCount based on remote MTU
+ uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3);
+ if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
+ maximumAttributeByteCount = maximumAttributeByteCount2;
+ }
+
+ // continuation state contains the offset into the complete response
+ uint16_t continuation_offset = 0;
+ if (continuationState[0] == 2){
+ continuation_offset = READ_NET_16(continuationState, 1);
+ }
+
+ // get service record
+ service_record_item_t * item = sdp_get_record_for_handle(serviceRecordHandle);
+ if (!item){
+ // service record handle doesn't exist
+ return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle
+ }
+
+
+ // AttributeList - starts at offset 7
+ uint16_t pos = 7;
+
+ if (continuation_offset == 0){
+
+ // get size of this record
+ uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
+
+ // store DES
+ de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
+ maximumAttributeByteCount -= 3;
+ pos += 3;
+ }
+
+ // copy maximumAttributeByteCount from record
+ uint16_t bytes_used;
+ int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
+ pos += bytes_used;
+
+ uint16_t attributeListByteCount = pos - 7;
+
+ if (complete) {
+ sdp_response_buffer[pos++] = 0;
+ } else {
+ continuation_offset += bytes_used;
+ sdp_response_buffer[pos++] = 2;
+ net_store_16(sdp_response_buffer, pos, continuation_offset);
+ pos += 2;
+ }
+
+ // header
+ sdp_response_buffer[0] = SDP_ServiceAttributeResponse;
+ net_store_16(sdp_response_buffer, 1, transaction_id);
+ net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
+ net_store_16(sdp_response_buffer, 5, attributeListByteCount);
+
+ return pos;
+}
+
+static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){
+ uint16_t total_response_size = 0;
+ linked_item_t *it;
+ for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){
+ service_record_item_t * item = (service_record_item_t *) it;
+
+ if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
+
+ // for all service records that match
+ total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList);
+ }
+ return total_response_size;
+}
+
+int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){
+
+ // SDP header before attribute sevice list: 7
+ // Continuation, worst case: 5
+
+ // get request details
+ uint16_t transaction_id = READ_NET_16(packet, 1);
+ // not used yet - uint16_t param_len = READ_NET_16(packet, 3);
+ uint8_t * serviceSearchPattern = &packet[5];
+ uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern);
+ uint16_t maximumAttributeByteCount = READ_NET_16(packet, 5 + serviceSearchPatternLen);
+ uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2];
+ uint16_t attributeIDListLen = de_get_len(attributeIDList);
+ uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen];
+
+ // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block
+ uint16_t maximumAttributeByteCount2 = remote_mtu - 12;
+ if (maximumAttributeByteCount2 < maximumAttributeByteCount) {
+ maximumAttributeByteCount = maximumAttributeByteCount2;
+ }
+
+ // continuation state contains: index of next service record to examine
+ // continuation state contains: byte offset into this service record
+ uint16_t continuation_service_index = 0;
+ uint16_t continuation_offset = 0;
+ if (continuationState[0] == 4){
+ continuation_service_index = READ_NET_16(continuationState, 1);
+ continuation_offset = READ_NET_16(continuationState, 3);
+ }
+
+ // printf("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u\n", continuation_service_index, continuation_offset, maximumAttributeByteCount);
+
+ // AttributeLists - starts at offset 7
+ uint16_t pos = 7;
+
+ // add DES with total size for first request
+ if (continuation_service_index == 0 && continuation_offset == 0){
+ uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList);
+ de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size);
+ // log_info("total response size %u\n", total_response_size);
+ pos += 3;
+ maximumAttributeByteCount -= 3;
+ }
+
+ // create attribute list
+ int first_answer = 1;
+ int continuation = 0;
+ uint16_t current_service_index = 0;
+ linked_item_t *it = (linked_item_t *) sdp_service_records;
+ for ( ; it ; it = it->next, ++current_service_index){
+ service_record_item_t * item = (service_record_item_t *) it;
+
+ if (current_service_index < continuation_service_index ) continue;
+ if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue;
+
+ if (continuation_offset == 0){
+
+ // get size of this record
+ uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList);
+
+ // stop if complete record doesn't fits into response but we already have a partial response
+ if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) {
+ continuation = 1;
+ break;
+ }
+
+ // store DES
+ de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size);
+ pos += 3;
+ maximumAttributeByteCount -= 3;
+ }
+
+ first_answer = 0;
+
+ // copy maximumAttributeByteCount from record
+ uint16_t bytes_used;
+ int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]);
+ pos += bytes_used;
+ maximumAttributeByteCount -= bytes_used;
+
+ if (complete) {
+ continuation_offset = 0;
+ continue;
+ }
+
+ continuation = 1;
+ continuation_offset += bytes_used;
+ break;
+ }
+
+ uint16_t attributeListsByteCount = pos - 7;
+
+ // Continuation State
+ if (continuation){
+ sdp_response_buffer[pos++] = 4;
+ net_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index);
+ pos += 2;
+ net_store_16(sdp_response_buffer, pos, continuation_offset);
+ pos += 2;
+ } else {
+ // complete
+ sdp_response_buffer[pos++] = 0;
+ }
+
+ // create SDP header
+ sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse;
+ net_store_16(sdp_response_buffer, 1, transaction_id);
+ net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload
+ net_store_16(sdp_response_buffer, 5, attributeListsByteCount);
+
+ return pos;
+}
+
+static void sdp_try_respond(void){
+ if (!sdp_response_size ) return;
+ if (!l2cap_cid) return;
+ if (!l2cap_can_send_packet_now(l2cap_cid)) return;
+
+ // update state before sending packet (avoid getting called when new l2cap credit gets emitted)
+ uint16_t size = sdp_response_size;
+ sdp_response_size = 0;
+ l2cap_send_internal(l2cap_cid, sdp_response_buffer, size);
+}
+
+// we assume that we don't get two requests in a row
+static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
+ uint16_t transaction_id;
+ SDP_PDU_ID_t pdu_id;
+ uint16_t remote_mtu;
+ // uint16_t param_len;
+
+ switch (packet_type) {
+
+ case L2CAP_DATA_PACKET:
+ pdu_id = (SDP_PDU_ID_t) packet[0];
+ transaction_id = READ_NET_16(packet, 1);
+ // param_len = READ_NET_16(packet, 3);
+ remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel);
+ // account for our buffer
+ if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){
+ remote_mtu = SDP_RESPONSE_BUFFER_SIZE;
+ }
+
+ // printf("SDP Request: type %u, transaction id %u, len %u, mtu %u\n", pdu_id, transaction_id, param_len, remote_mtu);
+ switch (pdu_id){
+
+ case SDP_ServiceSearchRequest:
+ sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu);
+ break;
+
+ case SDP_ServiceAttributeRequest:
+ sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu);
+ break;
+
+ case SDP_ServiceSearchAttributeRequest:
+ sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu);
+ break;
+
+ default:
+ sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax
+ break;
+ }
+
+ sdp_try_respond();
+
+ break;
+
+ case HCI_EVENT_PACKET:
+
+ switch (packet[0]) {
+
+ case L2CAP_EVENT_INCOMING_CONNECTION:
+ if (l2cap_cid) {
+ // CONNECTION REJECTED DUE TO LIMITED RESOURCES
+ l2cap_decline_connection_internal(channel, 0x0d);
+ break;
+ }
+ // accept
+ l2cap_cid = channel;
+ sdp_response_size = 0;
+ l2cap_accept_connection_internal(channel);
+ break;
+
+ case L2CAP_EVENT_CHANNEL_OPENED:
+ if (packet[2]) {
+ // open failed -> reset
+ l2cap_cid = 0;
+ }
+ break;
+
+ case L2CAP_EVENT_CREDITS:
+ case DAEMON_EVENT_HCI_PACKET_SENT:
+ sdp_try_respond();
+ break;
+
+ case L2CAP_EVENT_CHANNEL_CLOSED:
+ if (channel == l2cap_cid){
+ // reset
+ l2cap_cid = 0;
+ }
+ break;
+
+ default:
+ // other event
+ break;
+ }
+ break;
+
+ default:
+ // other packet type
+ break;
+ }
+}
+
