summaryrefslogtreecommitdiffstats
path: root/stack/sdp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2012-12-12 16:00:35 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2012-12-12 16:00:35 -0800
commit5738f83aeb59361a0a2eda2460113f6dc9194271 (patch)
treebf9fb1c890a681253207fe5d48e2cd56b94de3a7 /stack/sdp
downloadexternal_bluetooth_bluedroid-5738f83aeb59361a0a2eda2460113f6dc9194271.zip
external_bluetooth_bluedroid-5738f83aeb59361a0a2eda2460113f6dc9194271.tar.gz
external_bluetooth_bluedroid-5738f83aeb59361a0a2eda2460113f6dc9194271.tar.bz2
Snapshot cdeccf6fdd8c2d494ea2867cb37a025bf8879baf
Change-Id: Ia2de32ccb97a9641462c72363b0a8c4288f4f36d
Diffstat (limited to 'stack/sdp')
-rw-r--r--stack/sdp/sdp_api.c1475
-rw-r--r--stack/sdp/sdp_db.c962
-rw-r--r--stack/sdp/sdp_discovery.c1127
-rw-r--r--stack/sdp/sdp_main.c724
-rw-r--r--stack/sdp/sdp_server.c835
-rw-r--r--stack/sdp/sdp_utils.c1040
-rw-r--r--stack/sdp/sdpint.h329
7 files changed, 6492 insertions, 0 deletions
diff --git a/stack/sdp/sdp_api.c b/stack/sdp/sdp_api.c
new file mode 100644
index 0000000..4899aa8
--- /dev/null
+++ b/stack/sdp/sdp_api.c
@@ -0,0 +1,1475 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * this file contains SDP interface functions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bt_target.h"
+#include "gki.h"
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+
+#include "sdp_api.h"
+#include "sdpint.h"
+#include "btu.h"
+
+#include <cutils/log.h>
+#define info(fmt, ...) LOGI ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+#define debug(fmt, ...) LOGD ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+#define error(fmt, ...) LOGE ("## ERROR : %s: " fmt "##",__FUNCTION__, ## __VA_ARGS__)
+#define asrt(s) if(!(s)) LOGE ("## %s assert %s failed at line:%d ##",__FUNCTION__, #s, __LINE__)
+
+
+/**********************************************************************
+** C L I E N T F U N C T I O N P R O T O T Y P E S *
+***********************************************************************/
+
+/*******************************************************************************
+**
+** Function SDP_InitDiscoveryDb
+**
+** Description This function is called to initialize a discovery database.
+**
+** Parameters: p_db - (input) address of an area of memory where the
+** discovery database is managed.
+** len - (input) size (in bytes) of the memory
+** NOTE: This must be larger than sizeof(tSDP_DISCOVERY_DB)
+** num_uuid - (input) number of UUID filters applied
+** p_uuid_list - (input) list of UUID filters
+** num_attr - (input) number of attribute filters applied
+** p_attr_list - (input) list of attribute filters
+**
+**
+** Returns BOOLEAN
+** TRUE if successful
+** FALSE if one or more parameters are bad
+**
+*******************************************************************************/
+BOOLEAN SDP_InitDiscoveryDb (tSDP_DISCOVERY_DB *p_db, UINT32 len, UINT16 num_uuid,
+ tSDP_UUID *p_uuid_list, UINT16 num_attr, UINT16 *p_attr_list)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ UINT16 xx;
+
+ /* verify the parameters */
+ if (p_db == NULL || (sizeof (tSDP_DISCOVERY_DB) > len) ||
+ num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS)
+ {
+ SDP_TRACE_ERROR4("SDP_InitDiscoveryDb Illegal param: p_db 0x%x, len %d, num_uuid %d, num_attr %d",
+ (UINT32)p_db, len, num_uuid, num_attr);
+
+ return(FALSE);
+ }
+
+ memset (p_db, 0, (size_t)len);
+
+ p_db->mem_size = len - sizeof (tSDP_DISCOVERY_DB);
+ p_db->mem_free = p_db->mem_size;
+ p_db->p_first_rec = NULL;
+ p_db->p_free_mem = (UINT8 *)(p_db + 1);
+
+ for (xx = 0; xx < num_uuid; xx++)
+ p_db->uuid_filters[xx] = *p_uuid_list++;
+
+ p_db->num_uuid_filters = num_uuid;
+
+ for (xx = 0; xx < num_attr; xx++)
+ p_db->attr_filters[xx] = *p_attr_list++;
+
+ /* sort attributes */
+ sdpu_sort_attr_list( num_attr, p_db );
+
+ p_db->num_attr_filters = num_attr;
+#endif
+ return(TRUE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function SDP_CancelServiceSearch
+**
+** Description This function cancels an active query to an SDP server.
+**
+** Returns TRUE if discovery cancelled, FALSE if a matching activity is not found.
+**
+*******************************************************************************/
+BOOLEAN SDP_CancelServiceSearch (tSDP_DISCOVERY_DB *p_db)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb = sdpu_find_ccb_by_db (p_db);
+ if (!p_ccb)
+ return(FALSE);
+
+ sdp_disconnect (p_ccb, SDP_CANCEL);
+ p_ccb->disc_state = SDP_DISC_WAIT_CANCEL;
+#endif
+ return(TRUE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function SDP_ServiceSearchRequest
+**
+** Description This function queries an SDP server for information.
+**
+** Returns TRUE if discovery started, FALSE if failed.
+**
+*******************************************************************************/
+BOOLEAN SDP_ServiceSearchRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db,
+ tSDP_DISC_CMPL_CB *p_cb)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb;
+
+ /* Specific BD address */
+ p_ccb = sdp_conn_originate (p_bd_addr);
+
+ if (!p_ccb)
+ return(FALSE);
+
+ p_ccb->disc_state = SDP_DISC_WAIT_CONN;
+ p_ccb->p_db = p_db;
+ p_ccb->p_cb = p_cb;
+
+ return(TRUE);
+#else
+ return(FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_ServiceSearchAttributeRequest
+**
+** Description This function queries an SDP server for information.
+**
+** The difference between this API function and the function
+** SDP_ServiceSearchRequest is that this one does a
+** combined ServiceSearchAttributeRequest SDP function.
+** (This is for Unplug Testing)
+**
+** Returns TRUE if discovery started, FALSE if failed.
+**
+*******************************************************************************/
+BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db,
+ tSDP_DISC_CMPL_CB *p_cb)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb;
+
+ /* Specific BD address */
+ p_ccb = sdp_conn_originate (p_bd_addr);
+
+ if (!p_ccb)
+ return(FALSE);
+
+ p_ccb->disc_state = SDP_DISC_WAIT_CONN;
+ p_ccb->p_db = p_db;
+ p_ccb->p_cb = p_cb;
+
+ p_ccb->is_attr_search = TRUE;
+
+ return(TRUE);
+#else
+ return(FALSE);
+#endif
+}
+/*******************************************************************************
+**
+** Function SDP_ServiceSearchAttributeRequest2
+**
+** Description This function queries an SDP server for information.
+**
+** The difference between this API function and the function
+** SDP_ServiceSearchRequest is that this one does a
+** combined ServiceSearchAttributeRequest SDP function.
+** (This is for Unplug Testing)
+**
+** Returns TRUE if discovery started, FALSE if failed.
+**
+*******************************************************************************/
+BOOLEAN SDP_ServiceSearchAttributeRequest2 (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db,
+ tSDP_DISC_CMPL_CB2 *p_cb2, void * user_data)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb;
+
+ /* Specific BD address */
+ p_ccb = sdp_conn_originate (p_bd_addr);
+
+ if (!p_ccb)
+ return(FALSE);
+
+ p_ccb->disc_state = SDP_DISC_WAIT_CONN;
+ p_ccb->p_db = p_db;
+ p_ccb->p_cb2 = p_cb2;
+
+ p_ccb->is_attr_search = TRUE;
+ p_ccb->user_data = user_data;
+
+ return(TRUE);
+#else
+ return(FALSE);
+#endif
+}
+
+#if SDP_CLIENT_ENABLED == TRUE
+void SDP_SetIdleTimeout (BD_ADDR addr, UINT16 timeout)
+{
+}
+#endif
+
+/*******************************************************************************
+**
+** Function SDP_FindAttributeInDb
+**
+** Description This function queries an SDP database for a specific attribute.
+** If the p_start_rec pointer is NULL, it looks from the beginning
+** of the database, else it continues from the next record after
+** p_start_rec.
+**
+** Returns Pointer to matching record, or NULL
+**
+*******************************************************************************/
+tSDP_DISC_REC *SDP_FindAttributeInDb (tSDP_DISCOVERY_DB *p_db, UINT16 attr_id,
+ tSDP_DISC_REC *p_start_rec)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_REC *p_rec;
+ tSDP_DISC_ATTR *p_attr;
+
+ /* Must have a valid database */
+ if (p_db == NULL)
+ return(NULL);
+
+ if (!p_start_rec)
+ p_rec = p_db->p_first_rec;
+ else
+ p_rec = p_start_rec->p_next_rec;
+
+ while (p_rec)
+ {
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ if (p_attr->attr_id == attr_id)
+ return(p_rec);
+
+ p_attr = p_attr->p_next_attr;
+ }
+
+ p_rec = p_rec->p_next_rec;
+ }
+#endif
+ /* If here, no matching attribute found */
+ return(NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_FindAttributeInRec
+**
+** Description This function searches an SDP discovery record for a specific
+** attribute.
+**
+** Returns Pointer to matching attribute entry, or NULL
+**
+*******************************************************************************/
+tSDP_DISC_ATTR *SDP_FindAttributeInRec (tSDP_DISC_REC *p_rec, UINT16 attr_id)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr;
+
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ if (p_attr->attr_id == attr_id)
+ return(p_attr);
+
+ p_attr = p_attr->p_next_attr;
+ }
+#endif
+ /* If here, no matching attribute found */
+ return(NULL);
+}
+
+/*******************************************************************************
+**
+** Function SDP_FindServiceUUIDInRec
+**
+** Description This function is called to read the service UUID within a record
+** if there is any.
+**
+** Parameters: p_rec - pointer to a SDP record.
+** p_uuid - output parameter to save the UUID found.
+**
+** Returns TRUE if found, otherwise FALSE.
+**
+*******************************************************************************/
+BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID * p_uuid)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
+
+ p_attr = p_rec->p_first_attr;
+
+ while (p_attr)
+ {
+ if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ {
+ /* only support 16 bits UUID for now */
+ if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
+ {
+ p_uuid->len = 2;
+ p_uuid->uu.uuid16 = p_sattr->attr_value.v.u16;
+ }
+ return(TRUE);
+ }
+
+ /* Checking for Toyota G Block Car Kit:
+ ** This car kit puts an extra data element sequence
+ ** where the UUID is suppose to be!!!
+ */
+ else
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)
+ {
+ /* Look through data element sequence until no more UUIDs */
+ for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr)
+ {
+ /* Increment past this to see if the next attribut is UUID */
+ if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE)
+ /* only support 16 bits UUID for now */
+ && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2))
+ {
+ p_uuid->len = 2;
+ p_uuid->uu.uuid16 = p_extra_sattr->attr_value.v.u16;
+ return(TRUE);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ else if (p_attr->attr_id == ATTR_ID_SERVICE_ID)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
+ /* only support 16 bits UUID for now */
+ && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2))
+ {
+ p_uuid->len = 2;
+ p_uuid->uu.uuid16 = p_attr->attr_value.v.u16;
+ return(TRUE);
+ }
+ }
+ p_attr = p_attr->p_next_attr;
+ }
+ return FALSE;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_FindServiceUUIDInRec_128bit
+**
+** Description This function is called to read the 128-bit service UUID within a record
+** if there is any.
+**
+** Parameters: p_rec - pointer to a SDP record.
+** p_uuid - output parameter to save the UUID found.
+**
+** Returns TRUE if found, otherwise FALSE.
+**
+*******************************************************************************/
+BOOLEAN SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC *p_rec, tBT_UUID * p_uuid)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
+
+ p_attr = p_rec->p_first_attr;
+
+ while (p_attr)
+ {
+ if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ {
+ /* only support 128 bits UUID for now */
+ if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16)
+ {
+ p_uuid->len = 16;
+ memcpy(p_uuid->uu.uuid128, p_sattr->attr_value.v.array, MAX_UUID_SIZE);
+ }
+ return(TRUE);
+ }
+ }
+ break;
+ }
+ else if (p_attr->attr_id == ATTR_ID_SERVICE_ID)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
+ /* only support 128 bits UUID for now */
+ && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16))
+ {
+ p_uuid->len = 16;
+ memcpy(p_uuid->uu.uuid128, p_sattr->attr_value.v.array, MAX_UUID_SIZE);
+ return(TRUE);
+ }
+ }
+ p_attr = p_attr->p_next_attr;
+ }
+ return FALSE;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_FindServiceInDb
+**
+** Description This function queries an SDP database for a specific service.
+** If the p_start_rec pointer is NULL, it looks from the beginning
+** of the database, else it continues from the next record after
+** p_start_rec.
+**
+** Returns Pointer to record containing service class, or NULL
+**
+*******************************************************************************/
+tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, UINT16 service_uuid, tSDP_DISC_REC *p_start_rec)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_REC *p_rec;
+ tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
+
+ /* Must have a valid database */
+ if (p_db == NULL)
+ return(NULL);
+
+ if (!p_start_rec)
+ p_rec = p_db->p_first_rec;
+ else
+ p_rec = p_start_rec->p_next_rec;
+
+ while (p_rec)
+ {
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) ) {
+ printf("SDP_FindServiceInDb - p_sattr value = 0x%x serviceuuid = 0x%x\r\n", p_sattr->attr_value.v.u16, service_uuid);
+ if(service_uuid == UUID_SERVCLASS_HDP_PROFILE)
+ {
+ if( (p_sattr->attr_value.v.u16==UUID_SERVCLASS_HDP_SOURCE) || ( p_sattr->attr_value.v.u16==UUID_SERVCLASS_HDP_SOURCE))
+ {
+ printf("SDP_FindServiceInDb found HDP source or sink\n" );
+ return (p_rec);
+ }
+ }
+
+ }
+
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
+ /* for a specific uuid, or any one */
+ && ((p_sattr->attr_value.v.u16 == service_uuid) || service_uuid == 0))
+ {
+ return(p_rec);
+ }
+
+ /* Checking for Toyota G Block Car Kit:
+ ** This car kit puts an extra data element sequence
+ ** where the UUID is suppose to be!!!
+ */
+ else
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)
+ {
+ /* Look through data element sequence until no more UUIDs */
+ for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr)
+ {
+ /* Increment past this to see if the next attribut is UUID */
+ if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)
+ /* for a specific uuid, or any one */
+ && ((p_extra_sattr->attr_value.v.u16 == service_uuid) || (service_uuid == 0)))
+ {
+ return(p_rec);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ else if (p_attr->attr_id == ATTR_ID_SERVICE_ID)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2))
+ {
+ printf("SDP_FindServiceInDb - p_attr value = 0x%x serviceuuid= 0x%x \r\n", p_attr->attr_value.v.u16, service_uuid);
+ }
+
+ if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)
+ /* find a specific UUID or anyone */
+ && ((p_attr->attr_value.v.u16 == service_uuid) || service_uuid == 0))
+ return(p_rec);
+ }
+
+ p_attr = p_attr->p_next_attr;
+ }
+
+ p_rec = p_rec->p_next_rec;
+ }
+#endif
+ /* If here, no matching UUID found */
+ return(NULL);
+}
+
+/*******************************************************************************
+**
+** Function SDP_FindServiceInDb_128bit
+**
+** Description This function queries an SDP database for a specific service.
+** If the p_start_rec pointer is NULL, it looks from the beginning
+** of the database, else it continues from the next record after
+** p_start_rec.
+**
+** This function is kept separate from SDP_FindServiceInDb since
+** that API is expected to return only 16-bit UUIDs
+**
+** Returns Pointer to record containing service class, or NULL
+**
+*******************************************************************************/
+tSDP_DISC_REC *SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_start_rec)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_REC *p_rec;
+ tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr;
+
+ /* Must have a valid database */
+ if (p_db == NULL)
+ return(NULL);
+
+ if (!p_start_rec)
+ p_rec = p_db->p_first_rec;
+ else
+ p_rec = p_start_rec->p_next_rec;
+
+ while (p_rec)
+ {
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16))
+ {
+ return(p_rec);
+ }
+ }
+ break;
+ }
+ else if (p_attr->attr_id == ATTR_ID_SERVICE_ID)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16))
+ return(p_rec);
+ }
+
+ p_attr = p_attr->p_next_attr;
+ }
+
+ p_rec = p_rec->p_next_rec;
+ }
+#endif
+ /* If here, no matching UUID found */
+ return(NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_FindServiceUUIDInDb
+**
+** Description This function queries an SDP database for a specific service.
+** If the p_start_rec pointer is NULL, it looks from the beginning
+** of the database, else it continues from the next record after
+** p_start_rec.
+**
+** NOTE the only difference between this function and the previous function
+** "SDP_FindServiceInDb()" is that this function takes a tBT_UUID input
+**
+** Returns Pointer to record containing service class, or NULL
+**
+*******************************************************************************/
+tSDP_DISC_REC *SDP_FindServiceUUIDInDb (tSDP_DISCOVERY_DB *p_db, tBT_UUID *p_uuid, tSDP_DISC_REC *p_start_rec)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_REC *p_rec;
+ tSDP_DISC_ATTR *p_attr, *p_sattr;
+
+ /* Must have a valid database */
+ if (p_db == NULL)
+ return(NULL);
+
+ if (!p_start_rec)
+ p_rec = p_db->p_first_rec;
+ else
+ p_rec = p_start_rec->p_next_rec;
+
+ while (p_rec)
+ {
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ {
+
+ printf("uuid len=%d ", p_uuid->len);
+ if (p_uuid->len == 2)
+ {
+ printf("uuid=0x%x \n", p_uuid->uu.uuid16);
+ }
+ else
+ {
+ printf("\n");
+ }
+
+ if (sdpu_compare_uuid_with_attr (p_uuid, p_sattr))
+ return(p_rec);
+ }
+ }
+ break;
+ }
+ else if (p_attr->attr_id == ATTR_ID_SERVICE_ID)
+ {
+ if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE )
+ {
+ if (sdpu_compare_uuid_with_attr (p_uuid, p_attr))
+ return(p_rec);
+ }
+ }
+
+ p_attr = p_attr->p_next_attr;
+ }
+
+ p_rec = p_rec->p_next_rec;
+ }
+#endif /* CLIENT_ENABLED == TRUE */
+ /* If here, no matching UUID found */
+ return(NULL);
+}
+
+#if SDP_CLIENT_ENABLED == TRUE
+/*******************************************************************************
+**
+** Function sdp_fill_proto_elem
+**
+** Description This function retrieves the protocol element.
+**
+** Returns TRUE if found, FALSE if not
+** If found, the passed protocol list element is filled in.
+**
+*******************************************************************************/
+static BOOLEAN sdp_fill_proto_elem( tSDP_DISC_ATTR *p_attr, UINT16 layer_uuid,
+ tSDP_PROTOCOL_ELEM *p_elem)
+{
+ tSDP_DISC_ATTR *p_sattr;
+
+ /* Walk through the protocol descriptor list */
+ for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr)
+ {
+ /* Safety check - each entry should itself be a sequence */
+ if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
+ return(FALSE);
+
+ /* Now, see if the entry contains the layer we are interested in */
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ /* SDP_TRACE_DEBUG3 ("SDP - p_sattr 0x%x, layer_uuid:0x%x, u16:0x%x####",
+ p_sattr, layer_uuid, p_sattr->attr_value.v.u16); */
+
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
+ && (p_sattr->attr_value.v.u16 == layer_uuid))
+ {
+ /* Bingo. Now fill in the passed element */
+ p_elem->protocol_uuid = layer_uuid;
+ p_elem->num_params = 0;
+
+ /* Store the parameters, if any */
+ for (p_sattr = p_sattr->p_next_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) != UINT_DESC_TYPE)
+ break;
+
+ if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2)
+ p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u16;
+ else
+ p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u8;
+
+ if (p_elem->num_params >= SDP_MAX_PROTOCOL_PARAMS)
+ break;
+ }
+ return(TRUE);
+ }
+ }
+ }
+
+ return(FALSE);
+}
+#endif /* CLIENT_ENABLED == TRUE */
+
+/*******************************************************************************
+**
+** Function SDP_FindProtocolListElemInRec
+**
+** Description This function looks at a specific discovery record for a protocol
+** list element.
+**
+** Returns TRUE if found, FALSE if not
+** If found, the passed protocol list element is filled in.
+**
+*******************************************************************************/
+BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr;
+
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ /* Find the protocol descriptor list */
+ if ((p_attr->attr_id == ATTR_ID_PROTOCOL_DESC_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ return sdp_fill_proto_elem(p_attr, layer_uuid, p_elem);
+ }
+ p_attr = p_attr->p_next_attr;
+ }
+#endif
+ /* If here, no match found */
+ return(FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_FindAddProtoListsElemInRec
+**
+** Description This function looks at a specific discovery record for a protocol
+** list element.
+**
+** Returns TRUE if found, FALSE if not
+** If found, the passed protocol list element is filled in.
+**
+*******************************************************************************/
+BOOLEAN SDP_FindAddProtoListsElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr, *p_sattr;
+ BOOLEAN ret = FALSE;
+
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ /* Find the additional protocol descriptor list attribute */
+ if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ /* Safety check - each entry should itself be a sequence */
+ if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)
+ {
+ if ( (ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem)) == TRUE)
+ break;
+ }
+ }
+ return ret;
+ }
+ p_attr = p_attr->p_next_attr;
+ }
+#endif
+ /* If here, no match found */
+ return(FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_FindProfileVersionInRec
+**
+** Description This function looks at a specific discovery record for the
+** Profile list descriptor, and pulls out the version number.
+** The version number consists of an 8-bit major version and
+** an 8-bit minor version.
+**
+** Returns TRUE if found, FALSE if not
+** If found, the major and minor version numbers that were passed
+** in are filled in.
+**
+*******************************************************************************/
+BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, UINT16 profile_uuid, UINT16 *p_version)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISC_ATTR *p_attr, *p_sattr;
+
+ p_attr = p_rec->p_first_attr;
+ while (p_attr)
+ {
+ /* Find the profile descriptor list */
+ if ((p_attr->attr_id == ATTR_ID_BT_PROFILE_DESC_LIST)
+ && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE))
+ {
+ /* Walk through the protocol descriptor list */
+ for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr)
+ {
+ /* Safety check - each entry should itself be a sequence */
+ if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
+ return(FALSE);
+
+ /* Now, see if the entry contains the profile UUID we are interested in */
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) /* <- This is bytes, not size code! */
+ && (p_sattr->attr_value.v.u16 == profile_uuid))
+ {
+ /* Now fill in the major and minor numbers */
+ /* if the attribute matches the description for version (type UINT, size 2 bytes) */
+ p_sattr = p_sattr->p_next_attr;
+
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) &&
+ (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2))
+ {
+ /* The high order 8 bits is the major number, low order is the minor number (big endian) */
+ *p_version = p_sattr->attr_value.v.u16;
+
+ return(TRUE);
+ }
+ else
+ return(FALSE); /* The type and/or size was not valid for the profile list version */
+ }
+ }
+ }
+
+ return(FALSE);
+ }
+ p_attr = p_attr->p_next_attr;
+ }
+#endif /* CLIENT_ENABLED == TRUE */
+
+ /* If here, no match found */
+ return(FALSE);
+}
+
+/*******************************************************************************
+** Device Identification (DI) Client Functions
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** Function SDP_DiDiscover
+**
+** Description This function queries a remote device for DI information.
+**
+** Returns SDP_SUCCESS if query started successfully, else error
+**
+*******************************************************************************/
+UINT16 SDP_DiDiscover( BD_ADDR remote_device, tSDP_DISCOVERY_DB *p_db,
+ UINT32 len, tSDP_DISC_CMPL_CB *p_cb )
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ UINT16 result = SDP_DI_DISC_FAILED;
+ UINT16 num_uuids = 1;
+ UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
+
+ /* build uuid for db init */
+ tSDP_UUID init_uuid;
+ init_uuid.len = 2;
+ init_uuid.uu.uuid16 = di_uuid;
+
+ if ( SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL) )
+ if ( SDP_ServiceSearchRequest(remote_device, p_db, p_cb) )
+ result = SDP_SUCCESS;
+
+ return result;
+#else
+ return SDP_DI_DISC_FAILED;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_GetNumDiRecords
+**
+** Description Searches specified database for DI records
+**
+** Returns number of DI records found
+**
+*******************************************************************************/
+UINT8 SDP_GetNumDiRecords( tSDP_DISCOVERY_DB *p_db )
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ UINT8 num_records = 0;
+ tSDP_DISC_REC *p_curr_record = NULL;
+
+ do
+ {
+ p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION,
+ p_curr_record );
+ if ( p_curr_record )
+ num_records++;
+ }while ( p_curr_record );
+
+ return num_records;
+#else
+ return 0;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_GetDiRecord
+**
+** Description This function retrieves a remote device's DI record from
+** the specified database.
+**
+** Returns SDP_SUCCESS if record retrieved, else error
+**
+*******************************************************************************/
+UINT16 SDP_GetDiRecord( UINT8 get_record_index, tSDP_DI_GET_RECORD *p_device_info,
+ tSDP_DISCOVERY_DB *p_db )
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ UINT16 result = SDP_NO_DI_RECORD_FOUND;
+ UINT8 curr_record_index = 1;
+
+ tSDP_DISC_REC *p_curr_record = NULL;
+
+ /* find the requested SDP record in the discovery database */
+ do
+ {
+ p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION,
+ p_curr_record );
+ if ( p_curr_record )
+ {
+ if ( curr_record_index++ == get_record_index )
+ {
+ result = SDP_SUCCESS;
+ break;
+ }
+ }
+ }while ( p_curr_record );
+
+ if ( result == SDP_SUCCESS )
+ {
+ /* copy the information from the SDP record to the DI record */
+ tSDP_DISC_ATTR *p_curr_attr = NULL;
+
+ /* ClientExecutableURL is optional */
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_CLIENT_EXE_URL );
+ if ( p_curr_attr )
+ BCM_STRNCPY_S( p_device_info->rec.client_executable_url, sizeof(p_device_info->rec.client_executable_url),
+ (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN );
+ else
+ p_device_info->rec.client_executable_url[0] = '\0';
+
+ /* Service Description is optional */
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SERVICE_DESCRIPTION );
+ if ( p_curr_attr )
+ BCM_STRNCPY_S( p_device_info->rec.service_description, sizeof(p_device_info->rec.service_description),
+ (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN );
+ else
+ p_device_info->rec.service_description[0] = '\0';
+
+ /* DocumentationURL is optional */
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_DOCUMENTATION_URL );
+ if ( p_curr_attr )
+ BCM_STRNCPY_S( p_device_info->rec.documentation_url, sizeof(p_device_info->rec.documentation_url),
+ (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN );
+ else
+ p_device_info->rec.documentation_url[0] = '\0';
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SPECIFICATION_ID );
+ if ( p_curr_attr )
+ p_device_info->spec_id = p_curr_attr->attr_value.v.u16;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID );
+ if ( p_curr_attr )
+ p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID_SOURCE );
+ if ( p_curr_attr )
+ p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_ID );
+ if ( p_curr_attr )
+ p_device_info->rec.product = p_curr_attr->attr_value.v.u16;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_VERSION );
+ if ( p_curr_attr )
+ p_device_info->rec.version = p_curr_attr->attr_value.v.u16;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+
+ p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRIMARY_RECORD );
+ if ( p_curr_attr )
+ p_device_info->rec.primary_record = (BOOLEAN)p_curr_attr->attr_value.v.u8;
+ else
+ result = SDP_ERR_ATTR_NOT_PRESENT;
+ }
+
+ return result;
+#else /* SDP_CLIENT_ENABLED is FALSE */
+ return SDP_NO_DI_RECORD_FOUND;
+#endif
+}
+
+/*******************************************************************************
+** Device Identification (DI) Server Functions
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** Function SDP_SetLocalDiRecord
+**
+** Description This function adds a DI record to the local SDP database.
+**
+**
+**
+** Returns Returns SDP_SUCCESS if record added successfully, else error
+**
+*******************************************************************************/
+UINT16 SDP_SetLocalDiRecord( tSDP_DI_RECORD *p_device_info, UINT32 *p_handle )
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 result = SDP_SUCCESS;
+ UINT32 handle;
+ UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION;
+ UINT16 di_specid = BLUETOOTH_DI_SPECIFICATION;
+ UINT8 temp_u16[2];
+ UINT8 *p_temp;
+ UINT8 u8;
+
+ *p_handle = 0;
+ if ( p_device_info == NULL )
+ return SDP_ILLEGAL_PARAMETER;
+
+ /* if record is to be primary record, get handle to replace old primary */
+ if ( p_device_info->primary_record == TRUE && sdp_cb.server_db.di_primary_handle )
+ handle = sdp_cb.server_db.di_primary_handle;
+ else
+ {
+ if ( (handle = SDP_CreateRecord()) == 0 )
+ return SDP_NO_RESOURCES;
+ }
+
+ *p_handle = handle;
+
+ /* build the SDP entry */
+ /* Add the UUID to the Service Class ID List */
+ if ((SDP_AddServiceClassIdList(handle, 1, &di_uuid)) == FALSE)
+ result = SDP_DI_REG_FAILED;
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ p_temp = temp_u16;
+ UINT16_TO_BE_STREAM(p_temp, di_specid);
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_SPECIFICATION_ID,
+ UINT_DESC_TYPE, sizeof(di_specid),
+ temp_u16)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ /* optional - if string is null, do not add attribute */
+ if ( result == SDP_SUCCESS )
+ {
+ if ( p_device_info->client_executable_url[0] != '\0' )
+ {
+ if ( !((strlen(p_device_info->client_executable_url)+1 <= SDP_MAX_ATTR_LEN) &&
+ SDP_AddAttribute(handle, ATTR_ID_CLIENT_EXE_URL, URL_DESC_TYPE,
+ (UINT32)(strlen(p_device_info->client_executable_url)+1),
+ (UINT8 *)p_device_info->client_executable_url)) )
+ result = SDP_DI_REG_FAILED;
+ }
+ }
+
+ /* optional - if string is null, do not add attribute */
+ if ( result == SDP_SUCCESS )
+ {
+ if ( p_device_info->service_description[0] != '\0' )
+ {
+ if ( !((strlen(p_device_info->service_description)+1 <= SDP_MAX_ATTR_LEN) &&
+ SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION,
+ TEXT_STR_DESC_TYPE,
+ (UINT32)(strlen(p_device_info->service_description)+1),
+ (UINT8 *)p_device_info->service_description)) )
+ result = SDP_DI_REG_FAILED;
+ }
+ }
+
+ /* optional - if string is null, do not add attribute */
+ if ( result == SDP_SUCCESS )
+ {
+ if ( p_device_info->documentation_url[0] != '\0' )
+ {
+ if ( !((strlen(p_device_info->documentation_url)+1 <= SDP_MAX_ATTR_LEN) &&
+ SDP_AddAttribute(handle, ATTR_ID_DOCUMENTATION_URL, URL_DESC_TYPE,
+ (UINT32)(strlen(p_device_info->documentation_url)+1),
+ (UINT8 *)p_device_info->documentation_url)) )
+ result = SDP_DI_REG_FAILED;
+ }
+ }
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ p_temp = temp_u16;
+ UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor);
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID, UINT_DESC_TYPE,
+ sizeof(p_device_info->vendor), temp_u16)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ p_temp = temp_u16;
+ UINT16_TO_BE_STREAM (p_temp, p_device_info->product);
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_ID,
+ UINT_DESC_TYPE, sizeof(p_device_info->product), temp_u16)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ p_temp = temp_u16;
+ UINT16_TO_BE_STREAM (p_temp, p_device_info->version);
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_VERSION, UINT_DESC_TYPE,
+ sizeof(p_device_info->version), temp_u16)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ u8 = (UINT8)p_device_info->primary_record;
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_PRIMARY_RECORD,
+ BOOLEAN_DESC_TYPE, 1, &u8)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ /* mandatory */
+ if ( result == SDP_SUCCESS)
+ {
+ p_temp = temp_u16;
+ UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor_id_source);
+ if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID_SOURCE, UINT_DESC_TYPE,
+ sizeof(p_device_info->vendor_id_source), temp_u16)) )
+ result = SDP_DI_REG_FAILED;
+ }
+
+ if ( result != SDP_SUCCESS )
+ SDP_DeleteRecord( handle );
+ else if (p_device_info->primary_record == TRUE)
+ sdp_cb.server_db.di_primary_handle = handle;
+
+ return result;
+#else /* SDP_SERVER_ENABLED is FALSE */
+ return SDP_DI_REG_FAILED;
+#endif /* if SDP_SERVER_ENABLED */
+}
+
+/*******************************************************************************
+**
+** Function SDP_GetLocalDiRecord
+**
+** Description This function adds a DI record to the local SDP database.
+**
+** Fills in the device information of the record
+** p_handle - if p_handle == 0, the primary record is returned
+**
+** Returns Returns SDP_SUCCESS if record exists, else error
+**
+*******************************************************************************/
+UINT16 SDP_GetLocalDiRecord(tSDP_DI_GET_RECORD *p_device_info, UINT32 *p_handle )
+{
+ UINT16 result = SDP_NO_DI_RECORD_FOUND;
+
+#if SDP_SERVER_ENABLED == TRUE
+ tSDP_RECORD *p_rec;
+ tSDP_ATTRIBUTE *p_attr;
+ UINT8 *p_temp;
+ INT32 templen;
+
+ if (*p_handle == 0)
+ *p_handle = sdp_cb.server_db.di_primary_handle;
+
+ if ((p_rec = sdp_db_find_record(*p_handle)) != NULL)
+ {
+ memset(p_device_info, 0, sizeof(tSDP_DI_RECORD));
+
+ result = SDP_SUCCESS;
+
+ /* Retrieve the Specification ID */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SPECIFICATION_ID,
+ ATTR_ID_SPECIFICATION_ID)) != NULL)
+ {
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_UINT16 (p_device_info->spec_id, p_temp);
+ }
+
+ /* Retrieve the Vendor ID */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_VENDOR_ID,
+ ATTR_ID_VENDOR_ID)) != NULL)
+ {
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_UINT16 (p_device_info->rec.vendor, p_temp);
+ }
+
+ /* Retrieve the Product ID */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRODUCT_ID,
+ ATTR_ID_PRODUCT_ID)) != NULL)
+ {
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_UINT16 (p_device_info->rec.product, p_temp);
+ }
+
+ /* Retrieve the Version ID */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRODUCT_VERSION,
+ ATTR_ID_PRODUCT_VERSION)) != NULL)
+ {
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_UINT16 (p_device_info->rec.version, p_temp);
+ }
+
+ /* Retrieve the Vendor ID Source ID */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_VENDOR_ID_SOURCE,
+ ATTR_ID_VENDOR_ID_SOURCE)) != NULL)
+ {
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_UINT16 (p_device_info->rec.vendor_id_source, p_temp);
+ }
+
+ /* Retrieve the Primary Record */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRIMARY_RECORD,
+ ATTR_ID_PRIMARY_RECORD)) != NULL)
+ {
+ p_device_info->rec.primary_record = *p_attr->value_ptr;
+ }
+
+ /* Retrieve the Client Executable URL */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_CLIENT_EXE_URL,
+ ATTR_ID_CLIENT_EXE_URL)) != NULL)
+ {
+ templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN);
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.client_executable_url, templen);
+ }
+
+ /* Retrieve the Service Description */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SERVICE_DESCRIPTION,
+ ATTR_ID_SERVICE_DESCRIPTION)) != NULL)
+ {
+ templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN);
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.service_description, templen);
+ }
+
+ /* Retrieve the Documentation URL */
+ if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_DOCUMENTATION_URL,
+ ATTR_ID_DOCUMENTATION_URL)) != NULL)
+ {
+ templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN);
+ p_temp = p_attr->value_ptr;
+ BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.documentation_url, templen);
+ }
+ }
+ else
+ *p_handle = 0;
+#endif
+
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_SetTraceLevel
+**
+** Description This function sets the trace level for SDP. If called with
+** a value of 0xFF, it simply reads the current trace level.
+**
+** Returns the new (current) trace level
+**
+*******************************************************************************/
+UINT8 SDP_SetTraceLevel (UINT8 new_level)
+{
+ if (new_level != 0xFF)
+ sdp_cb.trace_level = new_level;
+
+ return(sdp_cb.trace_level);
+}
+
+#if SDP_FOR_JV_INCLUDED == TRUE
+/*******************************************************************************
+**
+** Function SDP_ConnOpen
+**
+** Description This function creates a connection to the SDP server on the
+** given device.
+**
+** Returns 0, if failed to initiate connection. Otherwise, the handle.
+**
+*******************************************************************************/
+UINT32 SDP_ConnOpen (UINT8 *p_bd_addr, tSDP_DISC_RES_CB *p_rcb,
+ tSDP_DISC_CMPL_CB *p_cb)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb;
+ UINT32 idx = 0;
+
+ if (!p_cb || !p_rcb)
+ return(idx);
+
+ /* Specific BD address */
+ p_ccb = sdp_conn_originate (p_bd_addr);
+
+ if (!p_ccb)
+ return(idx);
+
+ p_ccb->disc_state = SDP_DISC_WAIT_CONN;
+ p_ccb->p_db = (tSDP_DISCOVERY_DB *)p_rcb;
+ p_ccb->p_cb = p_cb;
+
+ p_ccb->is_attr_search = SDP_IS_PASS_THRU;
+
+ idx = (UINT32)(p_ccb - sdp_cb.ccb);
+ return(UINT32)(idx + 1);
+#else
+ return(0);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_WriteData
+**
+** Description This function sends data to the connected SDP server.
+**
+** Returns TRUE if data is sent, FALSE if failed.
+**
+*******************************************************************************/
+BOOLEAN SDP_WriteData (UINT32 handle, BT_HDR *p_msg)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb = NULL;
+
+ if (p_msg && (handle > 0) && (handle <= SDP_MAX_CONNECTIONS) )
+ {
+ p_ccb = &sdp_cb.ccb[handle - 1];
+ if ( (p_ccb->con_state == SDP_STATE_CONNECTED) &&
+ (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) )
+ {
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+ L2CA_DataWrite (p_ccb->connection_id, p_msg);
+ return TRUE;
+ }
+ }
+#endif
+ return FALSE;
+}
+
+/*******************************************************************************
+**
+** Function SDP_ConnClose
+**
+** Description This function is called to close a SDP connection.
+**
+** Parameters: handle - Handle of the connection returned by SDP_ConnOpen
+**
+** Returns TRUE if connection is closed, FALSE if failed to find the handle.
+**
+*******************************************************************************/
+BOOLEAN SDP_ConnClose (UINT32 handle)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ tCONN_CB *p_ccb = NULL;
+
+ if (handle > 0 && handle <= SDP_MAX_CONNECTIONS)
+ {
+ p_ccb = &sdp_cb.ccb[handle - 1];
+ sdp_disconnect (p_ccb, SDP_SUCCESS);
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+#endif
diff --git a/stack/sdp/sdp_db.c b/stack/sdp/sdp_db.c
new file mode 100644
index 0000000..45af000
--- /dev/null
+++ b/stack/sdp/sdp_db.c
@@ -0,0 +1,962 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * this file contains functions that handle the database
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bt_target.h"
+
+#include "gki.h"
+
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+
+#include "sdp_api.h"
+#include "sdpint.h"
+#include "wbt_api.h"
+
+#if SDP_SERVER_ENABLED == TRUE
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_his_uuid,
+ UINT16 his_len, int nest_level);
+
+
+/*******************************************************************************
+**
+** Function sdp_db_service_search
+**
+** Description This function searches for a record that contains the
+** specified UIDs. It is passed either NULL to start at the
+** beginning, or the previous record found.
+**
+** Returns Pointer to the record, or NULL if not found.
+**
+*******************************************************************************/
+tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq)
+{
+ UINT16 xx, yy;
+ tSDP_ATTRIBUTE *p_attr;
+ tSDP_RECORD *p_end = &sdp_cb.server_db.record[sdp_cb.server_db.num_records];
+
+ /* If NULL, start at the beginning, else start at the first specified record */
+ if (!p_rec)
+ p_rec = &sdp_cb.server_db.record[0];
+ else
+ p_rec++;
+
+ /* Look through the records. The spec says that a match occurs if */
+ /* the record contains all the passed UUIDs in it. */
+ for ( ; p_rec < p_end; p_rec++)
+ {
+ for (yy = 0; yy < p_seq->num_uids; yy++)
+ {
+ p_attr = &p_rec->attribute[0];
+ for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++)
+ {
+ if (p_attr->type == UUID_DESC_TYPE)
+ {
+ if (sdpu_compare_uuid_arrays (p_attr->value_ptr, p_attr->len,
+ &p_seq->uuid_entry[yy].value[0],
+ p_seq->uuid_entry[yy].len))
+ break;
+ }
+ else if (p_attr->type == DATA_ELE_SEQ_DESC_TYPE)
+ {
+ if (find_uuid_in_seq (p_attr->value_ptr, p_attr->len,
+ &p_seq->uuid_entry[yy].value[0],
+ p_seq->uuid_entry[yy].len, 0))
+ break;
+ }
+ }
+ /* If any UUID was not found, on to the next record */
+ if (xx == p_rec->num_attributes)
+ break;
+ }
+
+ /* If every UUID was found in the record, return the record */
+ if (yy == p_seq->num_uids)
+ return (p_rec);
+ }
+
+ /* If here, no more records found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function find_uuid_in_seq
+**
+** Description This function searches a data element sequenct for a UUID.
+**
+** Returns TRUE if found, else FALSE
+**
+*******************************************************************************/
+static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_uuid,
+ UINT16 uuid_len, int nest_level)
+{
+ UINT8 *p_end = p + seq_len;
+ UINT8 type;
+ UINT32 len;
+
+ /* A little safety check to avoid excessive recursion */
+ if (nest_level > 3)
+ return (FALSE);
+
+ while (p < p_end)
+ {
+ type = *p++;
+ p = sdpu_get_len_from_type (p, type, &len);
+ type = type >> 3;
+ if (type == UUID_DESC_TYPE)
+ {
+ if (sdpu_compare_uuid_arrays (p, len, p_uuid, uuid_len))
+ return (TRUE);
+ }
+ else if (type == DATA_ELE_SEQ_DESC_TYPE)
+ {
+ if (find_uuid_in_seq (p, len, p_uuid, uuid_len, nest_level + 1))
+ return (TRUE);
+ }
+ p = p + len;
+ }
+
+ /* If here, failed to match */
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function sdp_db_find_record
+**
+** Description This function searches for a record with a specific handle
+** It is passed the handle of the record.
+**
+** Returns Pointer to the record, or NULL if not found.
+**
+*******************************************************************************/
+tSDP_RECORD *sdp_db_find_record (UINT32 handle)
+{
+ tSDP_RECORD *p_rec;
+ tSDP_RECORD *p_end = &sdp_cb.server_db.record[sdp_cb.server_db.num_records];
+
+ /* Look through the records for the caller's handle */
+ for (p_rec = &sdp_cb.server_db.record[0]; p_rec < p_end; p_rec++)
+ {
+ if (p_rec->record_handle == handle)
+ return (p_rec);
+ }
+
+ /* Record with that handle not found. */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function sdp_db_find_attr_in_rec
+**
+** Description This function searches a record for specific attributes.
+** It is passed a pointer to the record. If the record contains
+** the specified attribute, (the caller may specify be a range
+** of attributes), the attribute is returned.
+**
+** Returns Pointer to the attribute, or NULL if not found.
+**
+*******************************************************************************/
+tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr,
+ UINT16 end_attr)
+{
+ tSDP_ATTRIBUTE *p_at;
+ UINT16 xx;
+
+ /* Note that the attributes in a record are assumed to be in sorted order */
+ for (xx = 0, p_at = &p_rec->attribute[0]; xx < p_rec->num_attributes;
+ xx++, p_at++)
+ {
+ if ((p_at->id >= start_attr) && (p_at->id <= end_attr))
+ return (p_at);
+ }
+
+ /* No matching attribute found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function sdp_compose_proto_list
+**
+** Description This function is called to compose a data sequence from
+** protocol element list struct pointer
+**
+** Returns the length of the data sequence
+**
+*******************************************************************************/
+static int sdp_compose_proto_list( UINT8 *p, UINT16 num_elem,
+ tSDP_PROTOCOL_ELEM *p_elem_list)
+{
+ UINT16 xx, yy, len;
+ BOOLEAN is_rfcomm_scn;
+ UINT8 *p_head = p;
+ UINT8 *p_len;
+
+ /* First, build the protocol list. This consists of a set of data element
+ ** sequences, one for each layer. Each layer sequence consists of layer's
+ ** UUID and optional parameters
+ */
+ for (xx = 0; xx < num_elem; xx++, p_elem_list++)
+ {
+ len = 3 + (p_elem_list->num_params * 3);
+ UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+
+ p_len = p;
+ *p++ = (UINT8) len;
+
+ UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, p_elem_list->protocol_uuid);
+
+ if (p_elem_list->protocol_uuid == UUID_PROTOCOL_RFCOMM)
+ is_rfcomm_scn = TRUE;
+ else
+ is_rfcomm_scn = FALSE;
+
+ for (yy = 0; yy < p_elem_list->num_params; yy++)
+ {
+ if (is_rfcomm_scn)
+ {
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE);
+ UINT8_TO_BE_STREAM (p, p_elem_list->params[yy]);
+
+ *p_len -= 1;
+ }
+ else
+ {
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, p_elem_list->params[yy]);
+ }
+ }
+ }
+ return (p - p_head);
+}
+
+#endif /* SDP_SERVER_ENABLED == TRUE */
+
+/*******************************************************************************
+**
+** Function SDP_CreateRecord
+**
+** Description This function is called to create a record in the database.
+** This would be through the SDP database maintenance API. The
+** record is created empty, teh application should then call
+** "add_attribute" to add the record's attributes.
+**
+** Returns Record handle if OK, else 0.
+**
+*******************************************************************************/
+UINT32 SDP_CreateRecord (void)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT32 handle;
+ UINT8 buf[4];
+ tSDP_DB *p_db = &sdp_cb.server_db;
+
+ /* First, check if there is a free record */
+ if (p_db->num_records < SDP_MAX_RECORDS)
+ {
+ memset (&p_db->record[p_db->num_records], 0,
+ sizeof (tSDP_RECORD));
+
+ /* We will use a handle of the first unreserved handle plus last record
+ ** number + 1 */
+ if (p_db->num_records)
+ handle = p_db->record[p_db->num_records - 1].record_handle + 1;
+ else
+ handle = 0x10000;
+
+ p_db->record[p_db->num_records].record_handle = handle;
+
+ p_db->num_records++;
+
+ /* Add the first attribute (the handle) automatically */
+ UINT32_TO_BE_FIELD (buf, handle);
+ SDP_AddAttribute (handle, ATTR_ID_SERVICE_RECORD_HDL, UINT_DESC_TYPE,
+ 4, buf);
+
+ return (p_db->record[p_db->num_records - 1].record_handle);
+ }
+#endif
+ return (0);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_DeleteRecord
+**
+** Description This function is called to add a record (or all records)
+** from the database. This would be through the SDP database
+** maintenance API.
+**
+** If a record handle of 0 is passed, all records are deleted.
+**
+** Returns TRUE if succeeded, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_DeleteRecord (UINT32 handle)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx, yy, zz;
+ tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0];
+
+ if (handle == 0 || sdp_cb.server_db.num_records == 0)
+ {
+ /* Delete all records in the database */
+ sdp_cb.server_db.num_records = 0;
+
+ /* require new DI record to be created in SDP_SetLocalDiRecord */
+ sdp_cb.server_db.di_primary_handle = 0;
+ sdp_cb.server_db.brcm_di_registered = 0;
+
+ return (TRUE);
+ }
+ else
+ {
+ /* Find the record in the database */
+ for (xx = 0; xx < sdp_cb.server_db.num_records; xx++, p_rec++)
+ {
+ if (p_rec->record_handle == handle)
+ {
+ /* Found it. Shift everything up one */
+ for (yy = xx; yy < sdp_cb.server_db.num_records; yy++, p_rec++)
+ {
+ *p_rec = *(p_rec + 1);
+
+ /* Adjust the attribute value pointer for each attribute */
+ for (zz = 0; zz < p_rec->num_attributes; zz++)
+ p_rec->attribute[zz].value_ptr -= sizeof(tSDP_RECORD);
+ }
+
+ sdp_cb.server_db.num_records--;
+
+ /* if we're deleting the primary DI record, clear the */
+ /* value in the control block */
+ if( sdp_cb.server_db.di_primary_handle == handle )
+ {
+ sdp_cb.server_db.di_primary_handle = 0;
+ sdp_cb.server_db.brcm_di_registered = 0;
+ }
+
+ return (TRUE);
+ }
+ }
+ }
+#endif
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddAttribute
+**
+** Description This function is called to add an attribute to a record.
+** This would be through the SDP database maintenance API.
+** If the attribute already exists in the record, it is replaced
+** with the new value.
+**
+** NOTE Attribute values must be passed as a Big Endian stream.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddAttribute (UINT32 handle, UINT16 attr_id, UINT8 attr_type,
+ UINT32 attr_len, UINT8 *p_val)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx, yy, zz;
+ tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0];
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ if (sdp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG)
+ {
+ if ((attr_type == UINT_DESC_TYPE) ||
+ (attr_type == TWO_COMP_INT_DESC_TYPE) ||
+ (attr_type == UUID_DESC_TYPE) ||
+ (attr_type == DATA_ELE_SEQ_DESC_TYPE) ||
+ (attr_type == DATA_ELE_ALT_DESC_TYPE))
+ {
+ UINT8 num_array[400];
+ UINT32 i;
+ UINT32 len = (attr_len > 200) ? 200 : attr_len;
+
+ num_array[0] ='\0';
+ for (i = 0; i < len; i++)
+ {
+ sprintf((char *)&num_array[i*2],"%02X",(UINT8)(p_val[i]));
+ }
+ SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s",
+ handle,attr_id,attr_type,attr_len,p_val,num_array);
+ }
+ else if (attr_type == BOOLEAN_DESC_TYPE)
+ {
+ SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%d",
+ handle,attr_id,attr_type,attr_len,p_val,*p_val);
+ }
+ else
+ {
+ SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s",
+ handle,attr_id,attr_type,attr_len,p_val,p_val);
+ }
+ }
+#endif
+
+ /* Find the record in the database */
+ for (zz = 0; zz < sdp_cb.server_db.num_records; zz++, p_rec++)
+ {
+ if (p_rec->record_handle == handle)
+ {
+ tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0];
+
+ /* Found the record. Now, see if the attribute already exists */
+ for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++)
+ {
+ /* The attribute exists. replace it */
+ if (p_attr->id == attr_id)
+ {
+ SDP_DeleteAttribute (handle, attr_id);
+ break;
+ }
+ if (p_attr->id > attr_id)
+ break;
+ }
+
+ if (p_rec->num_attributes == SDP_MAX_REC_ATTR)
+ return (FALSE);
+
+ /* If not found, see if we can allocate a new entry */
+ if (xx == p_rec->num_attributes)
+ p_attr = &p_rec->attribute[p_rec->num_attributes];
+ else
+ {
+ /* Since the attributes are kept in sorted order, insert ours here */
+ for (yy = p_rec->num_attributes; yy > xx; yy--)
+ p_rec->attribute[yy] = p_rec->attribute[yy - 1];
+ }
+
+ p_attr->id = attr_id;
+ p_attr->type = attr_type;
+ p_attr->len = attr_len;
+
+ if (p_rec->free_pad_ptr + attr_len >= SDP_MAX_PAD_LEN)
+ {
+ /* do truncate only for text string type descriptor */
+ if (attr_type == TEXT_STR_DESC_TYPE)
+ {
+ SDP_TRACE_WARNING2("SDP_AddAttribute: attr_len:%d too long. truncate to (%d)",
+ attr_len, SDP_MAX_PAD_LEN - p_rec->free_pad_ptr );
+
+ attr_len = SDP_MAX_PAD_LEN - p_rec->free_pad_ptr;
+ p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr] = '\0';
+ p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr+1] = '\0';
+ }
+ else
+ attr_len = 0;
+ }
+
+ if ((attr_len > 0) && (p_val != 0))
+ {
+ p_attr->len = attr_len;
+ memcpy (&p_rec->attr_pad[p_rec->free_pad_ptr], p_val, (size_t)attr_len);
+ p_attr->value_ptr = &p_rec->attr_pad[p_rec->free_pad_ptr];
+ p_rec->free_pad_ptr += attr_len;
+ }
+ else if ((attr_len == 0 && p_attr->len != 0) || /* if truncate to 0 length, simply don't add */
+ p_val == 0)
+ {
+ SDP_TRACE_ERROR2("SDP_AddAttribute fail, length exceed maximum: ID %d: attr_len:%d ",
+ attr_id, attr_len );
+ p_attr->id = p_attr->type = p_attr->len = 0;
+ return (FALSE);
+ }
+ p_rec->num_attributes++;
+
+ /*** Mark DI record as used by Broadcom ***/
+ if (handle == sdp_cb.server_db.di_primary_handle &&
+ attr_id == ATTR_ID_EXT_BRCM_VERSION)
+ sdp_cb.server_db.brcm_di_registered = TRUE;
+
+ return (TRUE);
+ }
+ }
+#endif
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddSequence
+**
+** Description This function is called to add a sequence to a record.
+** This would be through the SDP database maintenance API.
+** If the sequence already exists in the record, it is replaced
+** with the new sequence.
+**
+** NOTE Element values must be passed as a Big Endian stream.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddSequence (UINT32 handle, UINT16 attr_id, UINT16 num_elem,
+ UINT8 type[], UINT8 len[], UINT8 *p_val[])
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx;
+ UINT8 buff[SDP_MAX_ATTR_LEN * 2];
+ UINT8 *p;
+ UINT8 *p_head;
+
+ p = buff;
+ /* First, build the sequence */
+ for (xx = 0; xx < num_elem; xx++)
+ {
+ p_head = p;
+ switch (len[xx])
+ {
+ case 1:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_ONE_BYTE);
+ break;
+ case 2:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_TWO_BYTES);
+ break;
+ case 4:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_FOUR_BYTES);
+ break;
+ case 8:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_EIGHT_BYTES);
+ break;
+ case 16:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_SIXTEEN_BYTES);
+ break;
+ default:
+ UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_IN_NEXT_BYTE);
+ UINT8_TO_BE_STREAM (p, len[xx]);
+ break;
+ }
+
+ ARRAY_TO_BE_STREAM (p, p_val[xx], len[xx]);
+
+ if (p - buff > SDP_MAX_ATTR_LEN)
+ {
+ /* go back to before we add this element */
+ p = p_head;
+ if(p_head == buff)
+ {
+ /* the first element exceed the max length */
+ SDP_TRACE_ERROR0 ("SDP_AddSequence - too long(attribute is not added)!!");
+ return FALSE;
+ }
+ else
+ SDP_TRACE_ERROR2 ("SDP_AddSequence - too long, add %d elements of %d", xx, num_elem);
+ break;
+ }
+ }
+
+ return (SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE,
+ (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddUuidSequence
+**
+** Description This function is called to add a UUID sequence to a record.
+** This would be through the SDP database maintenance API.
+** If the sequence already exists in the record, it is replaced
+** with the new sequence.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddUuidSequence (UINT32 handle, UINT16 attr_id, UINT16 num_uuids,
+ UINT16 *p_uuids)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx;
+ UINT8 buff[SDP_MAX_ATTR_LEN * 2];
+ UINT8 *p = buff;
+ INT32 max_len = SDP_MAX_ATTR_LEN -3;
+
+ /* First, build the sequence */
+ for (xx = 0; xx < num_uuids ; xx++, p_uuids++)
+ {
+ UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, *p_uuids);
+
+ if((p - buff) > max_len)
+ {
+ SDP_TRACE_WARNING2 ("SDP_AddUuidSequence - too long, add %d uuids of %d", xx, num_uuids);
+ break;
+ }
+ }
+
+ return (SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE,
+ (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_AddProtocolList
+**
+** Description This function is called to add a protocol descriptor list to
+** a record. This would be through the SDP database maintenance API.
+** If the protocol list already exists in the record, it is replaced
+** with the new list.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddProtocolList (UINT32 handle, UINT16 num_elem,
+ tSDP_PROTOCOL_ELEM *p_elem_list)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT8 buff[SDP_MAX_ATTR_LEN * 2];
+ int offset;
+
+ offset = sdp_compose_proto_list(buff, num_elem, p_elem_list);
+
+ return (SDP_AddAttribute (handle, ATTR_ID_PROTOCOL_DESC_LIST,
+ DATA_ELE_SEQ_DESC_TYPE, (UINT32) offset, buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddAdditionProtoLists
+**
+** Description This function is called to add a protocol descriptor list to
+** a record. This would be through the SDP database maintenance API.
+** If the protocol list already exists in the record, it is replaced
+** with the new list.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddAdditionProtoLists (UINT32 handle, UINT16 num_elem,
+ tSDP_PROTO_LIST_ELEM *p_proto_list)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx;
+ UINT8 buff[SDP_MAX_ATTR_LEN * 2];
+ UINT8 *p = buff;
+ UINT8 *p_len;
+ int offset;
+
+ /* for each ProtocolDescriptorList */
+ for (xx = 0; xx < num_elem; xx++, p_proto_list++)
+ {
+ UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ p_len = p++;
+
+ offset = sdp_compose_proto_list(p, p_proto_list->num_elems,
+ p_proto_list->list_elem);
+ p += offset;
+
+ *p_len = (UINT8)(p - p_len - 1);
+ }
+
+ return (SDP_AddAttribute (handle, ATTR_ID_ADDITION_PROTO_DESC_LISTS,
+ DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function SDP_AddProfileDescriptorList
+**
+** Description This function is called to add a profile descriptor list to
+** a record. This would be through the SDP database maintenance API.
+** If the version already exists in the record, it is replaced
+** with the new one.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddProfileDescriptorList (UINT32 handle, UINT16 profile_uuid,
+ UINT16 version)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT8 buff[SDP_MAX_ATTR_LEN];
+ UINT8 *p = &buff[2];
+
+ /* First, build the profile descriptor list. This consists of a data element sequence. */
+ /* The sequence consists of profile's UUID and version number */
+ UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, profile_uuid);
+
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, version);
+
+ /* Add in type and length fields */
+ buff[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ buff[1] = (UINT8) (p - &buff[2]);
+
+ return (SDP_AddAttribute (handle, ATTR_ID_BT_PROFILE_DESC_LIST,
+ DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddLanguageBaseAttrIDList
+**
+** Description This function is called to add a language base attr list to
+** a record. This would be through the SDP database maintenance API.
+** If the version already exists in the record, it is replaced
+** with the new one.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddLanguageBaseAttrIDList (UINT32 handle, UINT16 lang,
+ UINT16 char_enc, UINT16 base_id)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT8 buff[SDP_MAX_ATTR_LEN];
+ UINT8 *p = buff;
+
+ /* First, build the language base descriptor list. This consists of a data */
+ /* element sequence. The sequence consists of 9 bytes (3 UINt16 fields) */
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, lang);
+
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, char_enc);
+
+ UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, base_id);
+
+ return (SDP_AddAttribute (handle, ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST,
+ DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_AddServiceClassIdList
+**
+** Description This function is called to add a service list to a record.
+** This would be through the SDP database maintenance API.
+** If the service list already exists in the record, it is replaced
+** with the new list.
+**
+** Returns TRUE if added OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN SDP_AddServiceClassIdList (UINT32 handle, UINT16 num_services,
+ UINT16 *p_service_uuids)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx;
+ UINT8 buff[SDP_MAX_ATTR_LEN * 2];
+ UINT8 *p = buff;
+
+ for (xx = 0; xx < num_services; xx++, p_service_uuids++)
+ {
+ UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p, *p_service_uuids);
+ }
+
+ return (SDP_AddAttribute (handle, ATTR_ID_SERVICE_CLASS_ID_LIST,
+ DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff));
+#else /* SDP_SERVER_ENABLED == FALSE */
+ return (FALSE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function SDP_DeleteAttribute
+**
+** Description This function is called to delete an attribute from a record.
+** This would be through the SDP database maintenance API.
+**
+** Returns TRUE if deleted OK, else FALSE if not found
+**
+*******************************************************************************/
+BOOLEAN SDP_DeleteAttribute (UINT32 handle, UINT16 attr_id)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 xx, yy;
+ tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0];
+ UINT8 *pad_ptr;
+ UINT32 len; /* Number of bytes in the entry */
+
+ /* Find the record in the database */
+ for (xx = 0; xx < sdp_cb.server_db.num_records; xx++, p_rec++)
+ {
+ if (p_rec->record_handle == handle)
+ {
+ tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0];
+
+ SDP_TRACE_API2("Deleting attr_id 0x%04x for handle 0x%x", attr_id, handle);
+ /* Found it. Now, find the attribute */
+ for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++)
+ {
+ if (p_attr->id == attr_id)
+ {
+ pad_ptr = p_attr->value_ptr;
+ len = p_attr->len;
+
+ if (len)
+ {
+ for (yy = 0; yy < p_rec->num_attributes; yy++)
+ {
+ if( p_rec->attribute[yy].value_ptr > pad_ptr )
+ p_rec->attribute[yy].value_ptr -= len;
+ }
+ }
+
+ /* Found it. Shift everything up one */
+ p_rec->num_attributes--;
+
+ for (yy = xx; yy < p_rec->num_attributes; yy++, p_attr++)
+ {
+ *p_attr = *(p_attr + 1);
+ }
+
+ /* adjust attribute values if needed */
+ if (len)
+ {
+ xx = (p_rec->free_pad_ptr - ((pad_ptr+len) -
+ &p_rec->attr_pad[0]));
+ for( yy=0; yy<xx; yy++, pad_ptr++)
+ *pad_ptr = *(pad_ptr+len);
+ p_rec->free_pad_ptr -= len;
+ }
+ return (TRUE);
+ }
+ }
+ }
+ }
+#endif
+ /* If here, not found */
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function SDP_ReadRecord
+**
+** Description This function is called to get the raw data of the record
+** with the given handle from the database.
+**
+** Returns -1, if the record is not found.
+** Otherwise, the offset (0 or 1) to start of data in p_data.
+**
+** The size of data copied into p_data is in *p_data_len.
+**
+*******************************************************************************/
+#if (SDP_RAW_DATA_INCLUDED == TRUE)
+INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len)
+{
+ INT32 len = 0; /* Number of bytes in the entry */
+ INT32 offset = -1; /* default to not found */
+#if SDP_SERVER_ENABLED == TRUE
+ tSDP_RECORD *p_rec;
+ UINT16 start = 0;
+ UINT16 end = 0xffff;
+ tSDP_ATTRIBUTE *p_attr;
+ UINT16 rem_len;
+ UINT8 *p_rsp;
+
+ /* Find the record in the database */
+ p_rec = sdp_db_find_record(handle);
+ if(p_rec && p_data && p_data_len)
+ {
+ p_rsp = &p_data[3];
+ while ( (p_attr = sdp_db_find_attr_in_rec (p_rec, start, end)) != NULL)
+ {
+ /* Check if attribute fits. Assume 3-byte value type/length */
+ rem_len = *p_data_len - (UINT16) (p_rsp - p_data);
+
+ if (p_attr->len > (UINT32)(rem_len - 6))
+ break;
+
+ p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
+
+ /* next attr id */
+ start = p_attr->id + 1;
+ }
+ len = (INT32) (p_rsp - p_data);
+
+ /* Put in the sequence header (2 or 3 bytes) */
+ if (len > 255)
+ {
+ offset = 0;
+ p_data[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
+ p_data[1] = (UINT8) ((len - 3) >> 8);
+ p_data[2] = (UINT8) (len - 3);
+ }
+ else
+ {
+ offset = 1;
+
+ p_data[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ p_data[2] = (UINT8) (len - 3);
+
+ len--;
+ }
+ *p_data_len = len;
+ }
+#endif
+ /* If here, not found */
+ return (offset);
+}
+#endif
+
+
+
diff --git a/stack/sdp/sdp_discovery.c b/stack/sdp/sdp_discovery.c
new file mode 100644
index 0000000..1ad1336
--- /dev/null
+++ b/stack/sdp/sdp_discovery.c
@@ -0,0 +1,1127 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * this file contains SDP discovery functions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bt_target.h"
+#include "gki.h"
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+#include "sdp_api.h"
+#include "sdpint.h"
+#include "btu.h"
+#include "btm_api.h"
+
+
+#ifndef SDP_DEBUG_RAW
+#define SDP_DEBUG_RAW FALSE
+#endif
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+#if SDP_CLIENT_ENABLED == TRUE
+static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len);
+static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len);
+static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len);
+static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end);
+static tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda);
+static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec,
+ UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level);
+
+/* Safety check in case we go crazy */
+#define MAX_NEST_LEVELS 5
+
+
+/*******************************************************************************
+**
+** Function sdpu_build_uuid_seq
+**
+** Description This function builds a UUID sequence from the list of
+** passed UUIDs. It is also passed the address of the output
+** buffer.
+**
+** Returns Pointer to next byte in the output buffer.
+**
+*******************************************************************************/
+static UINT8 *sdpu_build_uuid_seq (UINT8 *p_out, UINT16 num_uuids, tSDP_UUID *p_uuid_list)
+{
+ UINT16 xx;
+ UINT8 *p_len;
+
+ /* First thing is the data element header */
+ UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+
+ /* Remember where the length goes. Leave space for it. */
+ p_len = p_out;
+ p_out += 1;
+
+ /* Now, loop through and put in all the UUID(s) */
+ for (xx = 0; xx < num_uuids; xx++, p_uuid_list++)
+ {
+ if (p_uuid_list->len == 2)
+ {
+ UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid16);
+ }
+ else if (p_uuid_list->len == 4)
+ {
+ UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES);
+ UINT32_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid32);
+ }
+ else
+ {
+ UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES);
+ ARRAY_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid128, p_uuid_list->len);
+ }
+ }
+
+ /* Now, put in the length */
+ xx = (UINT16)(p_out - p_len - 1);
+ UINT8_TO_BE_STREAM (p_len, xx);
+
+ return (p_out);
+}
+
+/*******************************************************************************
+**
+** Function sdp_snd_service_search_req
+**
+** Description Send a service search request to the SDP server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_snd_service_search_req(tCONN_CB *p_ccb, UINT8 cont_len, UINT8 * p_cont)
+{
+ UINT8 *p, *p_start, *p_param_len;
+ BT_HDR *p_cmd;
+ UINT16 param_len;
+
+ /* Get a buffer to send the packet to L2CAP */
+ if ((p_cmd = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
+ {
+ sdp_disconnect (p_ccb, SDP_NO_RESOURCES);
+ return;
+ }
+
+ p_cmd->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8 *)(p_cmd + 1) + L2CAP_MIN_OFFSET;
+
+ /* Build a service search request packet */
+ UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_REQ);
+ UINT16_TO_BE_STREAM (p, p_ccb->transaction_id);
+ p_ccb->transaction_id++;
+
+ /* Skip the length, we need to add it at the end */
+ p_param_len = p;
+ p += 2;
+
+ /* Build the UID sequence. */
+#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
+ p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]);
+#else
+ p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters);
+#endif
+
+ /* Set max service record count */
+ UINT16_TO_BE_STREAM (p, sdp_cb.max_recs_per_search);
+
+ /* Set continuation state */
+ UINT8_TO_BE_STREAM (p, cont_len);
+
+ /* if this is not the first request */
+ if(cont_len && p_cont)
+ {
+ memcpy(p, p_cont, cont_len);
+ p += cont_len;
+ }
+
+ /* Go back and put the parameter length into the buffer */
+ param_len = (UINT16)(p - p_param_len - 2);
+ UINT16_TO_BE_STREAM (p_param_len, param_len);
+
+ p_ccb->disc_state = SDP_DISC_WAIT_HANDLES;
+
+ /* Set the length of the SDP data in the buffer */
+ p_cmd->len = (UINT16)(p - p_start);
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING2("sdp_snd_service_search_req cont_len :%d disc_state:%d",cont_len, p_ccb->disc_state);
+#endif
+
+
+ L2CA_DataWrite (p_ccb->connection_id, p_cmd);
+
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+
+}
+
+/*******************************************************************************
+**
+** Function sdp_disc_connected
+**
+** Description This function is called when an SDP discovery attempt is
+** connected.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_disc_connected (tCONN_CB *p_ccb)
+{
+
+#if SDP_FOR_JV_INCLUDED == TRUE
+ if (SDP_IS_PASS_THRU == p_ccb->is_attr_search)
+ {
+ tSDP_DISC_RES_CB *p_rcb = (tSDP_DISC_RES_CB *) p_ccb->p_db;
+ tSDP_DR_OPEN evt_data;
+ /* report connected */
+ p_ccb->disc_state = SDP_DISC_WAIT_PASS_THRU;
+ if (p_rcb)
+ {
+ memcpy(evt_data.peer_addr, p_ccb->device_address, BD_ADDR_LEN);
+ evt_data.peer_mtu = p_ccb->rem_mtu_size;
+ (*p_rcb)(SDP_EVT_OPEN, (void *)&evt_data);
+ }
+ }
+ else
+#endif
+ if (p_ccb->is_attr_search)
+ {
+ p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR;
+
+ process_service_search_attr_rsp (p_ccb, NULL, 0);
+ }
+ else
+ {
+ /* First step is to get a list of the handles from the server. */
+ /* We are not searching for a specific attribute, so we will */
+ /* first search for the service, then get all attributes of it */
+
+ p_ccb->num_handles = 0;
+ sdp_snd_service_search_req(p_ccb, 0, NULL);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function sdp_disc_server_rsp
+**
+** Description This function is called when there is a response from
+** the server.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg)
+{
+ UINT8 *p, rsp_pdu;
+ BOOLEAN invalid_pdu = TRUE;
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("sdp_disc_server_rsp disc_state:%d", p_ccb->disc_state);
+#endif
+
+ /* stop inactivity timer when we receive a response */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+#if SDP_FOR_JV_INCLUDED == TRUE
+ if(SDP_IS_PASS_THRU == p_ccb->is_attr_search)
+ {
+ tSDP_DISC_RES_CB *p_rcb = (tSDP_DISC_RES_CB *) p_ccb->p_db;
+ tSDP_DR_DATA data;
+ if (p_rcb)
+ {
+ data.p_data = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ data.data_len = p_msg->len;
+ (*p_rcb)(SDP_EVT_DATA_IND, (void *)&data);
+ }
+ return;
+ }
+#endif
+
+ /* Got a reply!! Check what we got back */
+ p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+
+ BE_STREAM_TO_UINT8 (rsp_pdu, p);
+
+ p_msg->len--;
+
+ switch (rsp_pdu)
+ {
+ case SDP_PDU_SERVICE_SEARCH_RSP:
+ if (p_ccb->disc_state == SDP_DISC_WAIT_HANDLES)
+ {
+ process_service_search_rsp (p_ccb, p, p_msg->len);
+ invalid_pdu = FALSE;
+ }
+ break;
+
+ case SDP_PDU_SERVICE_ATTR_RSP:
+ if (p_ccb->disc_state == SDP_DISC_WAIT_ATTR)
+ {
+ process_service_attr_rsp (p_ccb, p, p_msg->len);
+ invalid_pdu = FALSE;
+ }
+ break;
+
+ case SDP_PDU_SERVICE_SEARCH_ATTR_RSP:
+ if (p_ccb->disc_state == SDP_DISC_WAIT_SEARCH_ATTR)
+ {
+ process_service_search_attr_rsp (p_ccb, p, p_msg->len);
+ invalid_pdu = FALSE;
+ }
+ break;
+ }
+
+ if (invalid_pdu)
+ {
+ SDP_TRACE_WARNING2 ("SDP - Unexp. PDU: %d in state: %d", rsp_pdu, p_ccb->disc_state);
+ sdp_disconnect (p_ccb, SDP_GENERIC_ERROR);
+ }
+}
+
+/******************************************************************************
+**
+** Function process_service_search_rsp
+**
+** Description This function is called when there is a search response from
+** the server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len)
+{
+ UINT16 xx;
+ UINT16 total, cur_handles, orig;
+ UINT8 cont_len;
+
+ /* Skip transaction, and param len */
+ p_reply += 4;
+ BE_STREAM_TO_UINT16 (total, p_reply);
+ BE_STREAM_TO_UINT16 (cur_handles, p_reply);
+
+ orig = p_ccb->num_handles;
+ p_ccb->num_handles += cur_handles;
+ if (p_ccb->num_handles == 0)
+ {
+ SDP_TRACE_WARNING0 ("SDP - Rcvd ServiceSearchRsp, no matches");
+ sdp_disconnect (p_ccb, SDP_NO_RECS_MATCH);
+ return;
+ }
+
+ /* Save the handles that match. We will can only process a certain number. */
+ if (total > sdp_cb.max_recs_per_search)
+ total = sdp_cb.max_recs_per_search;
+ if (p_ccb->num_handles > sdp_cb.max_recs_per_search)
+ p_ccb->num_handles = sdp_cb.max_recs_per_search;
+
+ for (xx = orig; xx < p_ccb->num_handles; xx++)
+ BE_STREAM_TO_UINT32 (p_ccb->handles[xx], p_reply);
+
+ BE_STREAM_TO_UINT8 (cont_len, p_reply);
+ if(cont_len != 0)
+ {
+ if(cont_len > SDP_MAX_CONTINUATION_LEN)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE);
+ return;
+ }
+ /* stay in the same state */
+ sdp_snd_service_search_req(p_ccb, cont_len, p_reply);
+ }
+ else
+ {
+ /* change state */
+ p_ccb->disc_state = SDP_DISC_WAIT_ATTR;
+
+ /* Kick off the first attribute request */
+ process_service_attr_rsp (p_ccb, NULL, 0);
+ }
+}
+
+/*******************************************************************************
+**
+** Function sdp_copy_raw_data
+**
+** Description copy the raw data
+**
+**
+** Returns void
+**
+*******************************************************************************/
+#if (SDP_RAW_DATA_INCLUDED == TRUE)
+static void sdp_copy_raw_data (tCONN_CB *p_ccb, UINT16 len, BOOLEAN offset)
+{
+ unsigned int cpy_len;
+ UINT32 list_len;
+ UINT8 *p;
+ UINT8 * p_temp;
+ UINT8 type;
+ UINT32 delta_len = 0;
+
+#if (SDP_DEBUG_RAW == TRUE)
+ UINT8 num_array[SDP_MAX_LIST_BYTE_COUNT];
+ UINT32 i;
+
+ for (i = 0; i < p_ccb->list_len; i++)
+ {
+ sprintf((char *)&num_array[i*2],"%02X",(UINT8)(p_ccb->rsp_list[i]));
+ }
+ SDP_TRACE_WARNING1("result :%s",num_array);
+#endif
+
+ if(p_ccb->p_db->raw_data)
+ {
+ cpy_len = p_ccb->p_db->raw_size - p_ccb->p_db->raw_used;
+ list_len = p_ccb->list_len;
+ p_temp = p = &p_ccb->rsp_list[0];
+
+ if(offset)
+ {
+ type = *p++;
+ p = sdpu_get_len_from_type (p, type, &list_len);
+ }
+ if(list_len && list_len < cpy_len )
+ {
+ cpy_len = list_len;
+ }
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING4("list_len :%d cpy_len:%d raw_size:%d raw_used:%d",
+ list_len, cpy_len, p_ccb->p_db->raw_size, p_ccb->p_db->raw_used);
+#endif
+ memcpy (&p_ccb->p_db->raw_data[p_ccb->p_db->raw_used], p, cpy_len);
+ p_ccb->p_db->raw_used += cpy_len;
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function process_service_attr_rsp
+**
+** Description This function is called when there is a attribute response from
+** the server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len)
+{
+ UINT8 *p_start, *p_param_len;
+ UINT16 param_len, list_byte_count;
+ BOOLEAN cont_request_needed = FALSE;
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING2("process_service_attr_rsp len:%d raw inc:%d",
+ len, SDP_RAW_DATA_INCLUDED);
+#endif
+ /* If p_reply is NULL, we were called after the records handles were read */
+ if (p_reply)
+ {
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING4("ID & len: 0x%02x-%02x-%02x-%02x",
+ p_reply[0], p_reply[1], p_reply[2], p_reply[3]);
+#endif
+ /* Skip transaction ID and length */
+ p_reply += 4;
+
+ BE_STREAM_TO_UINT16 (list_byte_count, p_reply);
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("list_byte_count:%d", list_byte_count);
+#endif
+
+ /* Copy the response to the scratchpad. First, a safety check on the length */
+ if ((p_ccb->list_len + list_byte_count) > SDP_MAX_LIST_BYTE_COUNT)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE);
+ return;
+ }
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING2("list_len: %d, list_byte_count: %d",
+ p_ccb->list_len, list_byte_count);
+#endif
+ if (p_ccb->rsp_list == NULL)
+ {
+ p_ccb->rsp_list = (UINT8 *)GKI_getbuf (SDP_MAX_LIST_BYTE_COUNT);
+ if (p_ccb->rsp_list == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no gki buf to save rsp");
+ sdp_disconnect (p_ccb, SDP_NO_RESOURCES);
+ return;
+ }
+ }
+ memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, list_byte_count);
+ p_ccb->list_len += list_byte_count;
+ p_reply += list_byte_count;
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("list_len: %d(attr_rsp)", p_ccb->list_len);
+
+ /* Check if we need to request a continuation */
+ SDP_TRACE_WARNING2("*p_reply:%d(%d)", *p_reply, SDP_MAX_CONTINUATION_LEN);
+#endif
+ if (*p_reply)
+ {
+ if (*p_reply > SDP_MAX_CONTINUATION_LEN)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE);
+ return;
+ }
+ cont_request_needed = TRUE;
+ }
+ else
+ {
+
+#if (SDP_RAW_DATA_INCLUDED == TRUE)
+ SDP_TRACE_WARNING0("process_service_attr_rsp");
+ sdp_copy_raw_data (p_ccb, len, FALSE);
+#endif
+
+ /* Save the response in the database. Stop on any error */
+ if (!save_attr_seq (p_ccb, &p_ccb->rsp_list[0], &p_ccb->rsp_list[p_ccb->list_len]))
+ {
+ sdp_disconnect (p_ccb, SDP_DB_FULL);
+ return;
+ }
+ p_ccb->list_len = 0;
+ p_ccb->cur_handle++;
+ }
+ }
+
+ /* Now, ask for the next handle. Re-use the buffer we just got. */
+ if (p_ccb->cur_handle < p_ccb->num_handles)
+ {
+ BT_HDR *p_msg = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID);
+ UINT8 *p;
+
+ if (!p_msg)
+ {
+ sdp_disconnect (p_ccb, SDP_NO_RESOURCES);
+ return;
+ }
+
+ p_msg->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET;
+
+ /* Get all the attributes from the server */
+ UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_ATTR_REQ);
+ UINT16_TO_BE_STREAM (p, p_ccb->transaction_id);
+ p_ccb->transaction_id++;
+
+ /* Skip the length, we need to add it at the end */
+ p_param_len = p;
+ p += 2;
+
+ UINT32_TO_BE_STREAM (p, p_ccb->handles[p_ccb->cur_handle]);
+
+ /* Max attribute byte count */
+ UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size);
+
+ /* If no attribute filters, build a wildcard attribute sequence */
+ if (p_ccb->p_db->num_attr_filters)
+ p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters);
+ else
+ p = sdpu_build_attrib_seq (p, NULL, 0);
+
+ /* Was this a continuation request ? */
+ if (cont_request_needed)
+ {
+ memcpy (p, p_reply, *p_reply + 1);
+ p += *p_reply + 1;
+ }
+ else
+ UINT8_TO_BE_STREAM (p, 0);
+
+ /* Go back and put the parameter length into the buffer */
+ param_len = (UINT16)(p - p_param_len - 2);
+ UINT16_TO_BE_STREAM (p_param_len, param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_msg->len = (UINT16)(p - p_start);
+
+
+ L2CA_DataWrite (p_ccb->connection_id, p_msg);
+
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+ }
+ else
+ {
+ sdp_disconnect (p_ccb, SDP_SUCCESS);
+ return;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function process_service_search_attr_rsp
+**
+** Description This function is called when there is a search attribute
+** response from the server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len)
+{
+ UINT8 *p, *p_start, *p_end, *p_param_len;
+ UINT8 type;
+ UINT32 seq_len;
+ UINT16 param_len, lists_byte_count = 0;
+ BOOLEAN cont_request_needed = FALSE;
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("process_service_search_attr_rsp len:%d", len);
+#endif
+ /* If p_reply is NULL, we were called for the initial read */
+ if (p_reply)
+ {
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING4("ID & len: 0x%02x-%02x-%02x-%02x",
+ p_reply[0], p_reply[1], p_reply[2], p_reply[3]);
+#endif
+ /* Skip transaction ID and length */
+ p_reply += 4;
+
+ BE_STREAM_TO_UINT16 (lists_byte_count, p_reply);
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("lists_byte_count:%d", lists_byte_count);
+#endif
+
+ /* Copy the response to the scratchpad. First, a safety check on the length */
+ if ((p_ccb->list_len + lists_byte_count) > SDP_MAX_LIST_BYTE_COUNT)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE);
+ return;
+ }
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING2("list_len: %d, list_byte_count: %d",
+ p_ccb->list_len, lists_byte_count);
+#endif
+ if (p_ccb->rsp_list == NULL)
+ {
+ p_ccb->rsp_list = (UINT8 *)GKI_getbuf (SDP_MAX_LIST_BYTE_COUNT);
+ if (p_ccb->rsp_list == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no gki buf to save rsp");
+ sdp_disconnect (p_ccb, SDP_NO_RESOURCES);
+ return;
+ }
+ }
+ memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, lists_byte_count);
+ p_ccb->list_len += lists_byte_count;
+ p_reply += lists_byte_count;
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("list_len: %d(search_attr_rsp)", p_ccb->list_len);
+
+ /* Check if we need to request a continuation */
+ SDP_TRACE_WARNING2("*p_reply:%d(%d)", *p_reply, SDP_MAX_CONTINUATION_LEN);
+#endif
+ if (*p_reply)
+ {
+ if (*p_reply > SDP_MAX_CONTINUATION_LEN)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE);
+ return;
+ }
+
+ cont_request_needed = TRUE;
+ }
+ }
+
+#if (SDP_DEBUG_RAW == TRUE)
+ SDP_TRACE_WARNING1("cont_request_needed:%d", cont_request_needed);
+#endif
+ /* If continuation request (or first time request) */
+ if ((cont_request_needed) || (!p_reply))
+ {
+ BT_HDR *p_msg = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID);
+ UINT8 *p;
+
+ if (!p_msg)
+ {
+ sdp_disconnect (p_ccb, SDP_NO_RESOURCES);
+ return;
+ }
+
+ p_msg->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET;
+
+ /* Build a service search request packet */
+ UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_ATTR_REQ);
+ UINT16_TO_BE_STREAM (p, p_ccb->transaction_id);
+ p_ccb->transaction_id++;
+
+ /* Skip the length, we need to add it at the end */
+ p_param_len = p;
+ p += 2;
+
+ /* Build the UID sequence. */
+#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
+ p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]);
+#else
+ p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters);
+#endif
+
+ /* Max attribute byte count */
+ UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size);
+
+ /* If no attribute filters, build a wildcard attribute sequence */
+ if (p_ccb->p_db->num_attr_filters)
+ p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters);
+ else
+ p = sdpu_build_attrib_seq (p, NULL, 0);
+
+ /* No continuation for first request */
+ if (p_reply)
+ {
+ memcpy (p, p_reply, *p_reply + 1);
+ p += *p_reply + 1;
+ }
+ else
+ UINT8_TO_BE_STREAM (p, 0);
+
+ /* Go back and put the parameter length into the buffer */
+ param_len = p - p_param_len - 2;
+ UINT16_TO_BE_STREAM (p_param_len, param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_msg->len = p - p_start;
+
+
+ L2CA_DataWrite (p_ccb->connection_id, p_msg);
+
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+
+ return;
+ }
+
+
+ /*******************************************************************/
+ /* We now have the full response, which is a sequence of sequences */
+ /*******************************************************************/
+
+#if (SDP_RAW_DATA_INCLUDED == TRUE)
+ SDP_TRACE_WARNING0("process_service_search_attr_rsp");
+ sdp_copy_raw_data (p_ccb, len, TRUE);
+#endif
+
+ p = &p_ccb->rsp_list[0];
+
+ /* The contents is a sequence of attribute sequences */
+ type = *p++;
+
+ if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Wrong type: 0x%02x in attr_rsp", type);
+ return;
+ }
+ p = sdpu_get_len_from_type (p, type, &seq_len);
+
+ p_end = &p_ccb->rsp_list[p_ccb->list_len];
+
+ if ((p + seq_len) != p_end)
+ {
+ sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE);
+ return;
+ }
+
+ while (p < p_end)
+ {
+ p = save_attr_seq (p_ccb, p, &p_ccb->rsp_list[p_ccb->list_len]);
+ if (!p)
+ {
+ sdp_disconnect (p_ccb, SDP_DB_FULL);
+ return;
+ }
+ }
+
+ /* Since we got everything we need, disconnect the call */
+ sdp_disconnect (p_ccb, SDP_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function save_attr_seq
+**
+** Description This function is called when there is a response from
+** the server.
+**
+** Returns pointer to next byte or NULL if error
+**
+*******************************************************************************/
+static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end)
+{
+ UINT32 seq_len, attr_len;
+ UINT16 attr_id;
+ UINT8 type, *p_seq_end;
+ tSDP_DISC_REC *p_rec;
+
+ type = *p++;
+
+ if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Wrong type: 0x%02x in attr_rsp", type);
+ return (NULL);
+ }
+
+ p = sdpu_get_len_from_type (p, type, &seq_len);
+ if ((p + seq_len) > p_msg_end)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Bad len in attr_rsp %d", seq_len);
+ return (NULL);
+ }
+
+ /* Create a record */
+ p_rec = add_record (p_ccb->p_db, p_ccb->device_address);
+ if (!p_rec)
+ {
+ SDP_TRACE_WARNING0 ("SDP - DB full add_record");
+ return (NULL);
+ }
+
+ p_seq_end = p + seq_len;
+
+ while (p < p_seq_end)
+ {
+ /* First get the attribute ID */
+ type = *p++;
+ p = sdpu_get_len_from_type (p, type, &attr_len);
+ if (((type >> 3) != UINT_DESC_TYPE) || (attr_len != 2))
+ {
+ SDP_TRACE_WARNING2 ("SDP - Bad type: 0x%02x or len: %d in attr_rsp", type, attr_len);
+ return (NULL);
+ }
+ BE_STREAM_TO_UINT16 (attr_id, p);
+
+ /* Now, add the attribute value */
+ p = add_attr (p, p_ccb->p_db, p_rec, attr_id, NULL, 0);
+
+ if (!p)
+ {
+ SDP_TRACE_WARNING0 ("SDP - DB full add_attr");
+ return (NULL);
+ }
+ }
+
+ return (p);
+}
+
+
+/*******************************************************************************
+**
+** Function add_record
+**
+** Description This function allocates space for a record from the DB.
+**
+** Returns pointer to next byte in data stream
+**
+*******************************************************************************/
+tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda)
+{
+ tSDP_DISC_REC *p_rec;
+
+ /* See if there is enough space in the database */
+ if (p_db->mem_free < sizeof (tSDP_DISC_REC))
+ return (NULL);
+
+ p_rec = (tSDP_DISC_REC *) p_db->p_free_mem;
+ p_db->p_free_mem += sizeof (tSDP_DISC_REC);
+ p_db->mem_free -= sizeof (tSDP_DISC_REC);
+
+ p_rec->p_first_attr = NULL;
+ p_rec->p_next_rec = NULL;
+
+ memcpy (p_rec->remote_bd_addr, p_bda, BD_ADDR_LEN);
+
+ /* Add the record to the end of chain */
+ if (!p_db->p_first_rec)
+ p_db->p_first_rec = p_rec;
+ else
+ {
+ tSDP_DISC_REC *p_rec1 = p_db->p_first_rec;
+
+ while (p_rec1->p_next_rec)
+ p_rec1 = p_rec1->p_next_rec;
+
+ p_rec1->p_next_rec = p_rec;
+ }
+
+ return (p_rec);
+}
+
+#define SDP_ADDITIONAL_LIST_MASK 0x80
+/*******************************************************************************
+**
+** Function add_attr
+**
+** Description This function allocates space for an attribute from the DB
+** and copies the data into it.
+**
+** Returns pointer to next byte in data stream
+**
+*******************************************************************************/
+static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec,
+ UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level)
+{
+ tSDP_DISC_ATTR *p_attr;
+ UINT32 attr_len;
+ UINT32 total_len;
+ UINT16 attr_type;
+ UINT16 id;
+ UINT8 type;
+ UINT8 *p_end;
+ UINT8 is_additional_list = nest_level & SDP_ADDITIONAL_LIST_MASK;
+
+ nest_level &= ~(SDP_ADDITIONAL_LIST_MASK);
+
+ type = *p++;
+ p = sdpu_get_len_from_type (p, type, &attr_len);
+
+ attr_len &= SDP_DISC_ATTR_LEN_MASK;
+ attr_type = (type >> 3) & 0x0f;
+
+ /* See if there is enough space in the database */
+ if (attr_len > 4)
+ total_len = attr_len - 4 + (UINT16)sizeof (tSDP_DISC_ATTR);
+ else
+ total_len = sizeof (tSDP_DISC_ATTR);
+
+ /* Ensure it is a multiple of 4 */
+ total_len = (total_len + 3) & ~3;
+
+ /* See if there is enough space in the database */
+ if (p_db->mem_free < total_len)
+ return (NULL);
+
+ p_attr = (tSDP_DISC_ATTR *) p_db->p_free_mem;
+ p_attr->attr_id = attr_id;
+ p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12);
+ p_attr->p_next_attr = NULL;
+
+ /* Store the attribute value */
+ switch (attr_type)
+ {
+ case UINT_DESC_TYPE:
+ if( (is_additional_list != 0) && (attr_len == 2) )
+ {
+ BE_STREAM_TO_UINT16 (id, p);
+ if(id != ATTR_ID_PROTOCOL_DESC_LIST)
+ p -= 2;
+ else
+ {
+ /* Reserve the memory for the attribute now, as we need to add sub-attributes */
+ p_db->p_free_mem += sizeof (tSDP_DISC_ATTR);
+ p_db->mem_free -= sizeof (tSDP_DISC_ATTR);
+ p_end = p + attr_len;
+ total_len = 0;
+
+ /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:%d(list)", nest_level); */
+ if (nest_level >= MAX_NEST_LEVELS)
+ {
+ SDP_TRACE_ERROR0 ("SDP - attr nesting too deep");
+ return (p_end);
+ }
+
+ /* Now, add the list entry */
+ p = add_attr (p, p_db, p_rec, ATTR_ID_PROTOCOL_DESC_LIST, p_attr, (UINT8)(nest_level + 1));
+
+ break;
+ }
+ }
+ /* Case falls through */
+
+ case TWO_COMP_INT_DESC_TYPE:
+ switch (attr_len)
+ {
+ case 1:
+ p_attr->attr_value.v.u8 = *p++;
+ break;
+ case 2:
+ BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p);
+ break;
+ case 4:
+ BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p);
+ break;
+ default:
+ BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len);
+ break;
+ }
+ break;
+
+ case UUID_DESC_TYPE:
+ switch (attr_len)
+ {
+ case 2:
+ BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p);
+ break;
+ case 4:
+ BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p);
+ if (p_attr->attr_value.v.u32 < 0x10000)
+ {
+ attr_len = 2;
+ p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12);
+ p_attr->attr_value.v.u16 = (UINT16) p_attr->attr_value.v.u32;
+
+ }
+ break;
+ case 16:
+ /* See if we can compress his UUID down to 16 or 32bit UUIDs */
+ if (sdpu_is_base_uuid (p))
+ {
+ if ((p[0] == 0) && (p[1] == 0))
+ {
+ p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 2;
+ p += 2;
+ BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p);
+ p += MAX_UUID_SIZE - 4;
+ }
+ else
+ {
+ p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 4;
+ BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p);
+ p += MAX_UUID_SIZE - 4;
+ }
+ }
+ else
+ {
+ /* coverity[overrun-local] */
+ /*
+ Event overrun-local: Overrun of static array "p_attr->attr_value.v.array" of size 4 at position 15 with index variable "ijk"
+ False-positive: SDP uses scratch buffer to hold the attribute value.
+ The actual size of tSDP_DISC_ATVAL does not matter.
+ If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily
+ */
+ BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len);
+ }
+ break;
+ default:
+ SDP_TRACE_WARNING1 ("SDP - bad len in UUID attr: %d", attr_len);
+ return (p + attr_len);
+ }
+ break;
+
+ case DATA_ELE_SEQ_DESC_TYPE:
+ case DATA_ELE_ALT_DESC_TYPE:
+ /* Reserve the memory for the attribute now, as we need to add sub-attributes */
+ p_db->p_free_mem += sizeof (tSDP_DISC_ATTR);
+ p_db->mem_free -= sizeof (tSDP_DISC_ATTR);
+ p_end = p + attr_len;
+ total_len = 0;
+
+ /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:%d", nest_level); */
+ if (nest_level >= MAX_NEST_LEVELS)
+ {
+ SDP_TRACE_ERROR0 ("SDP - attr nesting too deep");
+ return (p_end);
+ }
+ if(is_additional_list != 0 || attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS)
+ nest_level |= SDP_ADDITIONAL_LIST_MASK;
+ /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:0x%x(finish)", nest_level); */
+
+ while (p < p_end)
+ {
+ /* Now, add the list entry */
+ p = add_attr (p, p_db, p_rec, 0, p_attr, (UINT8)(nest_level + 1));
+
+ if (!p)
+ return (NULL);
+ }
+ break;
+
+ case TEXT_STR_DESC_TYPE:
+ case URL_DESC_TYPE:
+ BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len);
+ break;
+
+ case BOOLEAN_DESC_TYPE:
+ switch (attr_len)
+ {
+ case 1:
+ p_attr->attr_value.v.u8 = *p++;
+ break;
+ default:
+ SDP_TRACE_WARNING1 ("SDP - bad len in boolean attr: %d", attr_len);
+ return (p + attr_len);
+ }
+ break;
+
+ default: /* switch (attr_type) */
+ break;
+ }
+
+ p_db->p_free_mem += total_len;
+ p_db->mem_free -= total_len;
+
+ /* Add the attribute to the end of the chain */
+ if (!p_parent_attr)
+ {
+ if (!p_rec->p_first_attr)
+ p_rec->p_first_attr = p_attr;
+ else
+ {
+ tSDP_DISC_ATTR *p_attr1 = p_rec->p_first_attr;
+
+ while (p_attr1->p_next_attr)
+ p_attr1 = p_attr1->p_next_attr;
+
+ p_attr1->p_next_attr = p_attr;
+ }
+ }
+ else
+ {
+ if (!p_parent_attr->attr_value.v.p_sub_attr)
+ {
+ p_parent_attr->attr_value.v.p_sub_attr = p_attr;
+ /* SDP_TRACE_DEBUG4 ("parent:0x%x(id:%d), ch:0x%x(id:%d)",
+ p_parent_attr, p_parent_attr->attr_id, p_attr, p_attr->attr_id); */
+ }
+ else
+ {
+ tSDP_DISC_ATTR *p_attr1 = p_parent_attr->attr_value.v.p_sub_attr;
+ /* SDP_TRACE_DEBUG4 ("parent:0x%x(id:%d), ch1:0x%x(id:%d)",
+ p_parent_attr, p_parent_attr->attr_id, p_attr1, p_attr1->attr_id); */
+
+ while (p_attr1->p_next_attr)
+ p_attr1 = p_attr1->p_next_attr;
+
+ p_attr1->p_next_attr = p_attr;
+ /* SDP_TRACE_DEBUG2 ("new ch:0x%x(id:%d)", p_attr, p_attr->attr_id); */
+ }
+ }
+
+ return (p);
+}
+
+#endif /* CLIENT_ENABLED == TRUE */
diff --git a/stack/sdp/sdp_main.c b/stack/sdp/sdp_main.c
new file mode 100644
index 0000000..14cd39b
--- /dev/null
+++ b/stack/sdp/sdp_main.c
@@ -0,0 +1,724 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains the main SDP functions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "bt_target.h"
+#include "gki.h"
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+
+#include "l2c_api.h"
+#include "l2cdefs.h"
+
+#include "btu.h"
+#include "btm_api.h"
+
+#include "sdp_api.h"
+#include "sdpint.h"
+
+
+/********************************************************************************/
+/* G L O B A L S D P D A T A */
+/********************************************************************************/
+#if SDP_DYNAMIC_MEMORY == FALSE
+tSDP_CB sdp_cb;
+#endif
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm,
+ UINT8 l2cap_id);
+static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
+static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
+
+#if SDP_CLIENT_ENABLED == TRUE
+static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result);
+static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result);
+#else
+#define sdp_connect_cfm NULL
+#define sdp_disconnect_cfm NULL
+#endif
+
+
+/*******************************************************************************
+**
+** Function sdp_init
+**
+** Description This function initializes the SDP unit.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_init (void)
+{
+ /* Clears all structures and local SDP database (if Server is enabled) */
+ memset (&sdp_cb, 0, sizeof (tSDP_CB));
+
+ /* Initialize the L2CAP configuration. We only care about MTU and flush */
+ sdp_cb.l2cap_my_cfg.mtu_present = TRUE;
+ sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE;
+ sdp_cb.l2cap_my_cfg.flush_to_present = TRUE;
+ sdp_cb.l2cap_my_cfg.flush_to = SDP_FLUSH_TO;
+
+ sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16;
+ sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS;
+
+#if SDP_SERVER_ENABLED == TRUE
+ /* Register with Security Manager for the specific security level */
+ if (!BTM_SetSecurityLevel (FALSE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
+ SDP_SECURITY_LEVEL, SDP_PSM, 0, 0))
+ {
+ SDP_TRACE_ERROR0 ("Security Registration Server failed");
+ return;
+ }
+#endif
+
+#if SDP_CLIENT_ENABLED == TRUE
+ /* Register with Security Manager for the specific security level */
+ if (!BTM_SetSecurityLevel (TRUE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
+ SDP_SECURITY_LEVEL, SDP_PSM, 0, 0))
+ {
+ SDP_TRACE_ERROR0 ("Security Registration for Client failed");
+ return;
+ }
+#endif
+
+#if defined(SDP_INITIAL_TRACE_LEVEL)
+ sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL;
+#else
+ sdp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */
+#endif
+
+ sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind;
+ sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm;
+ sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL;
+ sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind;
+ sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm;
+ sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind;
+ sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm;
+ sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL;
+ sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;
+ sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL;
+ sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL;
+
+ /* Now, register with L2CAP */
+ if (!L2CA_Register (SDP_PSM, &sdp_cb.reg_info))
+ {
+ SDP_TRACE_ERROR0 ("SDP Registration failed");
+ }
+}
+
+#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE)
+/*******************************************************************************
+**
+** Function sdp_set_max_attr_list_size
+**
+** Description This function sets the max attribute list size to use
+**
+** Returns void
+**
+*******************************************************************************/
+UINT16 sdp_set_max_attr_list_size (UINT16 max_size)
+{
+ if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16) )
+ max_size = sdp_cb.l2cap_my_cfg.mtu - 16;
+
+ sdp_cb.max_attr_list_size = max_size;
+
+ return sdp_cb.max_attr_list_size;
+}
+#endif
+
+/*******************************************************************************
+**
+** Function sdp_connect_ind
+**
+** Description This function handles an inbound connection indication
+** from L2CAP. This is the case where we are acting as a
+** server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
+{
+#if SDP_SERVER_ENABLED == TRUE
+ tCONN_CB *p_ccb;
+
+ /* Allocate a new CCB. Return if none available. */
+ if ((p_ccb = sdpu_allocate_ccb()) == NULL)
+ return;
+
+ /* Transition to the next appropriate state, waiting for config setup. */
+ p_ccb->con_state = SDP_STATE_CFG_SETUP;
+
+ /* Save the BD Address and Channel ID. */
+ memcpy (&p_ccb->device_address[0], bd_addr, sizeof (BD_ADDR));
+ p_ccb->connection_id = l2cap_cid;
+
+ /* Send response to the L2CAP layer. */
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
+ {
+ tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
+
+ if (cfg.fcr_present)
+ {
+ SDP_TRACE_DEBUG6("sdp_connect_ind: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u",
+ cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
+ cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps);
+ }
+
+ if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present
+ && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ /* FCR not desired; try again in basic mode */
+ cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+ cfg.fcr_present = FALSE;
+ L2CA_ConfigReq (l2cap_cid, &cfg);
+ }
+ }
+
+ SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x", p_ccb->connection_id);
+#else /* No server */
+ /* Reject the connection */
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0);
+#endif
+}
+
+#if SDP_CLIENT_ENABLED == TRUE
+/*******************************************************************************
+**
+** Function sdp_connect_cfm
+**
+** Description This function handles the connect confirm events
+** from L2CAP. This is the case when we are acting as a
+** client and have sent a connect request.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result)
+{
+ tCONN_CB *p_ccb;
+ tL2CAP_CFG_INFO cfg;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid);
+ return;
+ }
+
+ /* If the connection response contains success status, then */
+ /* Transition to the next state and startup the timer. */
+ if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP))
+ {
+ p_ccb->con_state = SDP_STATE_CFG_SETUP;
+
+ cfg = sdp_cb.l2cap_my_cfg;
+
+ if (cfg.fcr_present)
+ {
+ SDP_TRACE_DEBUG6("sdp_connect_cfm: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u",
+ cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit,
+ cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps);
+ }
+
+ if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present
+ && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ /* FCR not desired; try again in basic mode */
+ cfg.fcr_present = FALSE;
+ cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+ L2CA_ConfigReq (l2cap_cid, &cfg);
+ }
+
+ SDP_TRACE_EVENT1 ("SDP - got conn cnf, sent cfg req, CID: 0x%x", p_ccb->connection_id);
+ }
+ else
+ {
+ SDP_TRACE_WARNING2 ("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x", result, p_ccb->connection_id);
+
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_cb || p_ccb->p_cb2)
+ {
+ UINT16 err = -1;
+ if ((result == HCI_ERR_HOST_REJECT_SECURITY)
+ || (result == HCI_ERR_AUTH_FAILURE)
+ || (result == HCI_ERR_PAIRING_NOT_ALLOWED)
+ || (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED)
+ || (result == HCI_ERR_KEY_MISSING))
+ err = SDP_SECURITY_ERR;
+ else if (result == HCI_ERR_HOST_REJECT_DEVICE)
+ err = SDP_CONN_REJECTED;
+ else
+ err = SDP_CONN_FAILED;
+ if(p_ccb->p_cb)
+ (*p_ccb->p_cb)(err);
+ else if(p_ccb->p_cb2)
+ (*p_ccb->p_cb2)(err, p_ccb->user_data);
+
+ }
+ sdpu_release_ccb (p_ccb);
+ }
+}
+#endif /* SDP_CLIENT_ENABLED == TRUE */
+
+
+/*******************************************************************************
+**
+** Function sdp_config_ind
+**
+** Description This function processes the L2CAP configuration indication
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tCONN_CB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ /* Remember the remote MTU size */
+ if (!p_cfg->mtu_present)
+ {
+ /* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */
+ p_ccb->rem_mtu_size = (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE)?SDP_MTU_SIZE:L2CAP_DEFAULT_MTU;
+ }
+ else
+ {
+ if (p_cfg->mtu > SDP_MTU_SIZE)
+ p_ccb->rem_mtu_size = SDP_MTU_SIZE;
+ else
+ p_ccb->rem_mtu_size = p_cfg->mtu;
+ }
+
+ /* For now, always accept configuration from the other side */
+ p_cfg->flush_to_present = FALSE;
+ p_cfg->mtu_present = FALSE;
+ p_cfg->result = L2CAP_CFG_OK;
+
+ /* Check peer config request against our rfcomm configuration */
+ if (p_cfg->fcr_present)
+ {
+ /* Reject the window size if it is bigger than we want it to be */
+ if (p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE)
+ {
+ if (sdp_cb.l2cap_my_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE
+ && p_cfg->fcr.tx_win_sz > sdp_cb.l2cap_my_cfg.fcr.tx_win_sz)
+ {
+ p_cfg->fcr.tx_win_sz = sdp_cb.l2cap_my_cfg.fcr.tx_win_sz;
+ p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with SMALLER TX WINDOW");
+ }
+
+ /* Reject if locally we want basic and they don't */
+ if (sdp_cb.l2cap_my_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE)
+ {
+ /* Ask for a new setup */
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with BASIC mode");
+ }
+ /* Remain in configure state and give the peer our desired configuration */
+ if (p_cfg->result != L2CAP_CFG_OK)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: 0x%x", l2cap_cid);
+ L2CA_ConfigRsp (l2cap_cid, p_cfg);
+ return;
+ }
+ }
+ else /* We agree with peer's request */
+ p_cfg->fcr_present = FALSE;
+ }
+
+ L2CA_ConfigRsp (l2cap_cid, p_cfg);
+
+ SDP_TRACE_EVENT1 ("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
+
+ p_ccb->con_flags |= SDP_FLAGS_HIS_CFG_DONE;
+
+ if (p_ccb->con_flags & SDP_FLAGS_MY_CFG_DONE)
+ {
+ p_ccb->con_state = SDP_STATE_CONNECTED;
+
+ if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
+ sdp_disc_connected (p_ccb);
+ else
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+ }
+
+}
+
+
+/*******************************************************************************
+**
+** Function sdp_config_cfm
+**
+** Description This function processes the L2CAP configuration confirmation
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tCONN_CB *p_ccb;
+
+ SDP_TRACE_EVENT2 ("SDP - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result);
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ /* For now, always accept configuration from the other side */
+ if (p_cfg->result == L2CAP_CFG_OK)
+ {
+ p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE;
+
+ if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE)
+ {
+ p_ccb->con_state = SDP_STATE_CONNECTED;
+
+ if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
+ sdp_disc_connected (p_ccb);
+ else
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+ }
+ }
+ else
+ {
+ /* If peer has rejected FCR and suggested basic then try basic */
+ if (p_cfg->fcr_present)
+ {
+ tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
+ cfg.fcr_present = FALSE;
+ L2CA_ConfigReq (l2cap_cid, &cfg);
+
+ /* Remain in configure state */
+ return;
+ }
+
+#if SDP_CLIENT_ENABLED == TRUE
+ sdp_disconnect(p_ccb, SDP_CFG_FAILED);
+#endif
+ }
+}
+
+/*******************************************************************************
+**
+** Function sdp_disconnect_ind
+**
+** Description This function handles a disconnect event from L2CAP. If
+** requested to, we ack the disconnect before dropping the CCB
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
+{
+ tCONN_CB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ if (ack_needed)
+ L2CA_DisconnectRsp (l2cap_cid);
+
+ SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
+#if SDP_CLIENT_ENABLED == TRUE
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_cb)
+ (*p_ccb->p_cb) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
+ SDP_SUCCESS : SDP_CONN_FAILED));
+ else if (p_ccb->p_cb2)
+ (*p_ccb->p_cb2) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
+ SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data);
+
+#endif
+ sdpu_release_ccb (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function sdp_data_ind
+**
+** Description This function is called when data is received from L2CAP.
+** if we are the originator of the connection, we are the SDP
+** client, and the received message is queued up for the client.
+**
+** If we are the destination of the connection, we are the SDP
+** server, so the message is passed to the server processing
+** function.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
+{
+ tCONN_CB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL)
+ {
+ if (p_ccb->con_state == SDP_STATE_CONNECTED)
+ {
+ if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
+ sdp_disc_server_rsp (p_ccb, p_msg);
+ else
+ sdp_server_handle_client_req (p_ccb, p_msg);
+ }
+ else
+ {
+ SDP_TRACE_WARNING2 ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x",
+ p_ccb->con_state, l2cap_cid);
+ }
+ }
+ else
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
+ }
+
+ GKI_freebuf (p_msg);
+}
+
+
+#if SDP_CLIENT_ENABLED == TRUE
+/*******************************************************************************
+**
+** Function sdp_conn_originate
+**
+** Description This function is called from the API to originate a
+** connection.
+**
+** Returns void
+**
+*******************************************************************************/
+tCONN_CB* sdp_conn_originate (UINT8 *p_bd_addr)
+{
+ tCONN_CB *p_ccb;
+ UINT16 cid;
+
+ /* Allocate a new CCB. Return if none available. */
+ if ((p_ccb = sdpu_allocate_ccb()) == NULL)
+ {
+ SDP_TRACE_WARNING0 ("SDP - no spare CCB for orig");
+ return (NULL);
+ }
+
+ SDP_TRACE_EVENT0 ("SDP - Originate started");
+
+ /* We are the originator of this connection */
+ p_ccb->con_flags |= SDP_FLAGS_IS_ORIG;
+
+ /* Save the BD Address and Channel ID. */
+ memcpy (&p_ccb->device_address[0], p_bd_addr, sizeof (BD_ADDR));
+
+ /* Transition to the next appropriate state, waiting for connection confirm. */
+ p_ccb->con_state = SDP_STATE_CONN_SETUP;
+
+// btla-specific ++
+#ifndef ANDROID_APP_INCLUDED /* Skip for Android: Do not need to set out_service for sdp, since sdp does not use sec. Prevents over-writing service_rec of a connection already in progress */
+ BTM_SetOutService(p_bd_addr, BTM_SEC_SERVICE_SDP_SERVER, 0);
+#endif
+// btla-specific --
+
+ cid = L2CA_ConnectReq (SDP_PSM, p_bd_addr);
+
+ /* Check if L2CAP started the connection process */
+ if (cid != 0)
+ {
+ p_ccb->connection_id = cid;
+
+ return (p_ccb);
+ }
+ else
+ {
+ SDP_TRACE_WARNING0 ("SDP - Originate failed");
+ sdpu_release_ccb (p_ccb);
+ return (NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function sdp_disconnect
+**
+** Description This function disconnects a connection.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_disconnect (tCONN_CB*p_ccb, UINT16 reason)
+{
+#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
+
+ /* If we are browsing for multiple UUIDs ... */
+ if ((p_ccb->con_state == SDP_STATE_CONNECTED)
+ && (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
+ && ((reason == SDP_SUCCESS) || (reason == SDP_NO_RECS_MATCH)))
+ {
+ /* If the browse found something, do no more searching */
+ if ((p_ccb->cur_uuid_idx == 0) && (p_ccb->p_db->p_first_rec))
+ p_ccb->cur_uuid_idx = p_ccb->p_db->num_uuid_filters;
+
+ while (++p_ccb->cur_uuid_idx < p_ccb->p_db->num_uuid_filters)
+ {
+ /* Check we have not already found the UUID (maybe through browse) */
+ if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len == 2)
+ && (SDP_FindServiceInDb (p_ccb->p_db,
+ p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].uu.uuid16,
+ NULL)))
+ continue;
+
+ if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len > 2)
+ && (SDP_FindServiceUUIDInDb (p_ccb->p_db,
+ &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx], NULL)))
+ continue;
+
+ p_ccb->cur_handle = 0;
+
+ SDP_TRACE_EVENT1 ("SDP - looking for for more, CID: 0x%x",
+ p_ccb->connection_id);
+
+ sdp_disc_connected (p_ccb);
+ return;
+ }
+ }
+
+ if ((reason == SDP_NO_RECS_MATCH) && (p_ccb->p_db->p_first_rec))
+ reason = SDP_SUCCESS;
+
+#endif
+
+ SDP_TRACE_EVENT1 ("SDP - disconnect CID: 0x%x", p_ccb->connection_id);
+
+ /* Check if we have a connection ID */
+ if (p_ccb->connection_id != 0)
+ {
+ L2CA_DisconnectReq (p_ccb->connection_id);
+ p_ccb->disconnect_reason = reason;
+ }
+
+ /* If at setup state, we may not get callback ind from L2CAP */
+ /* Call user callback immediately */
+ if (p_ccb->con_state == SDP_STATE_CONN_SETUP)
+ {
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_cb)
+ (*p_ccb->p_cb) (reason);
+ else if (p_ccb->p_cb2)
+ (*p_ccb->p_cb2) (reason, p_ccb->user_data);
+
+ sdpu_release_ccb (p_ccb);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function sdp_disconnect_cfm
+**
+** Description This function handles a disconnect confirm event from L2CAP.
+**
+** Returns void
+**
+*******************************************************************************/
+static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
+{
+ tCONN_CB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
+
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_cb)
+ (*p_ccb->p_cb) (p_ccb->disconnect_reason);
+ else if (p_ccb->p_cb2)
+ (*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data);
+
+
+ sdpu_release_ccb (p_ccb);
+}
+
+#endif /* SDP_CLIENT_ENABLED == TRUE */
+
+/*******************************************************************************
+**
+** Function sdp_conn_timeout
+**
+** Description This function processes a timeout. Currently, we simply send
+** a disconnect request to L2CAP.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_conn_timeout (tCONN_CB*p_ccb)
+{
+ SDP_TRACE_EVENT2 ("SDP - CCB timeout in state: %d CID: 0x%x",
+ p_ccb->con_state, p_ccb->connection_id);
+
+ L2CA_DisconnectReq (p_ccb->connection_id);
+#if SDP_CLIENT_ENABLED == TRUE
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_cb)
+ (*p_ccb->p_cb) (SDP_CONN_FAILED);
+ else if (p_ccb->p_cb2)
+ (*p_ccb->p_cb2) (SDP_CONN_FAILED, p_ccb->user_data);
+#endif
+ sdpu_release_ccb (p_ccb);
+}
+
+
+
+
diff --git a/stack/sdp/sdp_server.c b/stack/sdp/sdp_server.c
new file mode 100644
index 0000000..5736b8d
--- /dev/null
+++ b/stack/sdp/sdp_server.c
@@ -0,0 +1,835 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains functions that handle the SDP server functions.
+ * This is mainly dealing with client requests
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+#include "btu.h"
+
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+
+#include "sdp_api.h"
+#include "sdpint.h"
+
+
+#if SDP_SERVER_ENABLED == TRUE
+
+/* Maximum number of bytes to reserve out of SDP MTU for response data */
+#define SDP_MAX_SERVICE_RSPHDR_LEN 12
+#define SDP_MAX_SERVATTR_RSPHDR_LEN 10
+#define SDP_MAX_ATTR_RSPHDR_LEN 10
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end);
+
+static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end);
+
+static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end);
+
+
+/********************************************************************************/
+/* E R R O R T E X T S T R I N G S */
+/* */
+/* The default is to have no text string, but we allow the strings to be */
+/* configured in target.h if people want them. */
+/********************************************************************************/
+#ifndef SDP_TEXT_BAD_HEADER
+#define SDP_TEXT_BAD_HEADER NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_PDU
+#define SDP_TEXT_BAD_PDU NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_UUID_LIST
+#define SDP_TEXT_BAD_UUID_LIST NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_HANDLE
+#define SDP_TEXT_BAD_HANDLE NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_ATTR_LIST
+#define SDP_TEXT_BAD_ATTR_LIST NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_CONT_LEN
+#define SDP_TEXT_BAD_CONT_LEN NULL
+#endif
+
+#ifndef SDP_TEXT_BAD_CONT_INX
+#define SDP_TEXT_BAD_CONT_INX NULL
+#endif
+
+/*******************************************************************************
+**
+** Function sdp_server_handle_client_req
+**
+** Description This is the main dispatcher of the SDP server. It is called
+** when any data is received from L2CAP, and dispatches the
+** request to the appropriate handler.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg)
+{
+ UINT8 *p_req = (UINT8 *) (p_msg + 1) + p_msg->offset;
+ UINT8 *p_req_end = p_req + p_msg->len;
+ UINT8 pdu_id;
+ UINT16 trans_num, param_len;
+
+
+ /* Start inactivity timer */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);
+
+ /* The first byte in the message is the pdu type */
+ pdu_id = *p_req++;
+
+ /* Extract the transaction number and parameter length */
+ BE_STREAM_TO_UINT16 (trans_num, p_req);
+ BE_STREAM_TO_UINT16 (param_len, p_req);
+
+ if ((p_req + param_len) > p_req_end)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER);
+ return;
+ }
+
+ switch (pdu_id)
+ {
+ case SDP_PDU_SERVICE_SEARCH_REQ:
+ process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end);
+ break;
+
+ case SDP_PDU_SERVICE_ATTR_REQ:
+ process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
+ break;
+
+ case SDP_PDU_SERVICE_SEARCH_ATTR_REQ:
+ process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end);
+ break;
+
+ default:
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU);
+ SDP_TRACE_WARNING1 ("SDP - server got unknown PDU: 0x%x", pdu_id);
+ break;
+ }
+}
+
+
+
+/*******************************************************************************
+**
+** Function process_service_search
+**
+** Description This function handles a service search request from the
+** client. It builds a reply message with info from the database,
+** and sends the reply back to the client.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end)
+{
+ UINT16 max_replies, cur_handles, rem_handles, cont_offset;
+ tSDP_UUID_SEQ uid_seq;
+ UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
+ UINT16 rsp_param_len, num_rsp_handles, xx;
+ UINT32 rsp_handles[SDP_MAX_RECORDS];
+ tSDP_RECORD *p_rec = NULL;
+ BT_HDR *p_buf;
+ BOOLEAN is_cont = FALSE;
+
+ p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
+
+ if ((!p_req) || (!uid_seq.num_uids))
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
+ return;
+ }
+
+ /* Get the max replies we can send. Cap it at our max anyways. */
+ BE_STREAM_TO_UINT16 (max_replies, p_req);
+
+ if (max_replies > SDP_MAX_RECORDS)
+ max_replies = SDP_MAX_RECORDS;
+
+ /* Get a list of handles that match the UUIDs given to us */
+ for (num_rsp_handles = 0; num_rsp_handles < max_replies; )
+ {
+ p_rec = sdp_db_service_search (p_rec, &uid_seq);
+
+ if (p_rec)
+ rsp_handles[num_rsp_handles++] = p_rec->record_handle;
+ else
+ break;
+ }
+
+ /* Check if this is a continuation request */
+ if (*p_req)
+ {
+ if (*p_req++ != SDP_CONTINUATION_LEN)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
+ SDP_TEXT_BAD_CONT_LEN);
+ return;
+ }
+ BE_STREAM_TO_UINT16 (cont_offset, p_req);
+
+ if (cont_offset != p_ccb->cont_offset)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE,
+ SDP_TEXT_BAD_CONT_INX);
+ return;
+ }
+
+ rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */
+ }
+ else
+ {
+ rem_handles = num_rsp_handles;
+ cont_offset = 0;
+ }
+
+ /* Calculate how many handles will fit in one PDU */
+ cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4);
+
+ if (rem_handles <= cur_handles)
+ cur_handles = rem_handles;
+ else /* Continuation is set */
+ {
+ p_ccb->cont_offset += cur_handles;
+ is_cont = TRUE;
+ }
+
+ /* Get a buffer to use to build the response */
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
+ return;
+ }
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
+
+ /* Start building a rsponse */
+ UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP);
+ UINT16_TO_BE_STREAM (p_rsp, trans_num);
+
+ /* Skip the length, we need to add it at the end */
+ p_rsp_param_len = p_rsp;
+ p_rsp += 2;
+
+ /* Put in total and current number of handles, and handles themselves */
+ UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles);
+ UINT16_TO_BE_STREAM (p_rsp, cur_handles);
+
+/* SDP_TRACE_DEBUG5("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d",
+ num_rsp_handles, cur_handles, cont_offset,
+ cont_offset + cur_handles-1, is_cont); */
+ for (xx = cont_offset; xx < cont_offset + cur_handles; xx++)
+ UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]);
+
+ if (is_cont)
+ {
+ UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
+ UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
+ }
+ else
+ UINT8_TO_BE_STREAM (p_rsp, 0);
+
+ /* Go back and put the parameter length into the buffer */
+ rsp_param_len = p_rsp - p_rsp_param_len - 2;
+ UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_buf->len = p_rsp - p_rsp_start;
+
+
+ /* Send the buffer through L2CAP */
+ L2CA_DataWrite (p_ccb->connection_id, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function process_service_attr_req
+**
+** Description This function handles an attribute request from the client.
+** It builds a reply message with info from the database,
+** and sends the reply back to the client.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end)
+{
+ UINT16 max_list_len, len_to_send, cont_offset;
+ INT16 rem_len;
+ tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
+ UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
+ UINT16 rsp_param_len, xx;
+ UINT32 rec_handle;
+ tSDP_RECORD *p_rec;
+ tSDP_ATTRIBUTE *p_attr;
+ BT_HDR *p_buf;
+ BOOLEAN is_cont = FALSE;
+ UINT16 attr_len;
+
+ /* Extract the record handle */
+ BE_STREAM_TO_UINT32 (rec_handle, p_req);
+
+ if (p_req > p_req_end)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
+ return;
+ }
+
+ /* Get the max list length we can send. Cap it at MTU size minus overhead */
+ BE_STREAM_TO_UINT16 (max_list_len, p_req);
+
+ if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN))
+ max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN;
+
+ p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
+
+ if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end))
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
+ return;
+ }
+
+ memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
+
+ /* Find a record with the record handle */
+ p_rec = sdp_db_find_record (rec_handle);
+ if (!p_rec)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE);
+ return;
+ }
+
+ /* Check if this is a continuation request */
+ if (*p_req)
+ {
+ if (*p_req++ != SDP_CONTINUATION_LEN)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
+ return;
+ }
+ BE_STREAM_TO_UINT16 (cont_offset, p_req);
+
+ if (cont_offset != p_ccb->cont_offset)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
+ return;
+ }
+
+ if (!p_ccb->rsp_list)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
+ return;
+ }
+ is_cont = TRUE;
+
+ /* Initialise for continuation response */
+ p_rsp = &p_ccb->rsp_list[0];
+ attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
+ }
+ else
+ {
+ /* Get a scratch buffer to store response */
+ if (!p_ccb->rsp_list)
+ {
+ p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
+ if (p_ccb->rsp_list == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
+ return;
+ }
+ }
+
+ p_ccb->cont_offset = 0;
+ p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
+
+ /* Reset continuation parameters in p_ccb */
+ p_ccb->cont_info.prev_sdp_rec = NULL;
+ p_ccb->cont_info.next_attr_index = 0;
+ p_ccb->cont_info.attr_offset = 0;
+ }
+
+ /* Search for attributes that match the list given to us */
+ for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
+ {
+ p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
+
+ if (p_attr)
+ {
+ /* Check if attribute fits. Assume 3-byte value type/length */
+ rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
+
+ /* just in case */
+ if (rem_len <= 0)
+ {
+ p_ccb->cont_info.next_attr_index = xx;
+ p_ccb->cont_info.next_attr_start_id = p_attr->id;
+ break;
+ }
+
+ attr_len = sdpu_get_attrib_entry_len(p_attr);
+ /* if there is a partial attribute pending to be sent */
+ if (p_ccb->cont_info.attr_offset)
+ {
+ p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
+ &p_ccb->cont_info.attr_offset);
+
+ /* If the partial attrib could not been fully added yet */
+ if (p_ccb->cont_info.attr_offset != attr_len)
+ break;
+ else /* If the partial attrib has been added in full by now */
+ p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
+ }
+ else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
+ {
+ if (attr_len >= MAX_ATTR_LEN)
+ {
+ SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
+ return;
+ }
+
+ /* add the partial attribute if possible */
+ p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
+ &p_ccb->cont_info.attr_offset);
+
+ p_ccb->cont_info.next_attr_index = xx;
+ p_ccb->cont_info.next_attr_start_id = p_attr->id;
+ break;
+ }
+ else /* build the whole attribute */
+ p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
+
+ /* If doing a range, stick with this one till no more attributes found */
+ if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
+ {
+ /* Update for next time through */
+ attr_seq.attr_entry[xx].start = p_attr->id + 1;
+
+ xx--;
+ }
+ }
+ }
+ /* If all the attributes have been accomodated in p_rsp,
+ reset next_attr_index */
+ if (xx == attr_seq.num_attr)
+ p_ccb->cont_info.next_attr_index = 0;
+
+ len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
+ cont_offset = 0;
+
+ if (!is_cont)
+ {
+ p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3;
+ /* Put in the sequence header (2 or 3 bytes) */
+ if (p_ccb->list_len > 255)
+ {
+ p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
+ p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
+ p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
+ }
+ else
+ {
+ cont_offset = 1;
+
+ p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
+
+ p_ccb->list_len--;
+ len_to_send--;
+ }
+ }
+
+ /* Get a buffer to use to build the response */
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
+ return;
+ }
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
+
+ /* Start building a rsponse */
+ UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP);
+ UINT16_TO_BE_STREAM (p_rsp, trans_num);
+
+ /* Skip the parameter length, add it when we know the length */
+ p_rsp_param_len = p_rsp;
+ p_rsp += 2;
+
+ UINT16_TO_BE_STREAM (p_rsp, len_to_send);
+
+ memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
+ p_rsp += len_to_send;
+
+ p_ccb->cont_offset += len_to_send;
+
+ /* If anything left to send, continuation needed */
+ if (p_ccb->cont_offset < p_ccb->list_len)
+ {
+ is_cont = TRUE;
+
+ UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
+ UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
+ }
+ else
+ UINT8_TO_BE_STREAM (p_rsp, 0);
+
+ /* Go back and put the parameter length into the buffer */
+ rsp_param_len = p_rsp - p_rsp_param_len - 2;
+ UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_buf->len = p_rsp - p_rsp_start;
+
+
+ /* Send the buffer through L2CAP */
+ L2CA_DataWrite (p_ccb->connection_id, p_buf);
+}
+
+
+
+/*******************************************************************************
+**
+** Function process_service_search_attr_req
+**
+** Description This function handles a combined service search and attribute
+** read request from the client. It builds a reply message with
+** info from the database, and sends the reply back to the client.
+**
+** Returns void
+**
+*******************************************************************************/
+static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num,
+ UINT16 param_len, UINT8 *p_req,
+ UINT8 *p_req_end)
+{
+ UINT16 max_list_len;
+ INT16 rem_len;
+ UINT16 len_to_send, cont_offset;
+ tSDP_UUID_SEQ uid_seq;
+ UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
+ UINT16 rsp_param_len, xx;
+ tSDP_RECORD *p_rec;
+ tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
+ tSDP_ATTRIBUTE *p_attr;
+ BT_HDR *p_buf;
+ BOOLEAN maxxed_out = FALSE, is_cont = FALSE;
+ UINT8 *p_seq_start;
+ UINT16 seq_len, attr_len;
+
+ /* Extract the UUID sequence to search for */
+ p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq);
+
+ if ((!p_req) || (!uid_seq.num_uids))
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST);
+ return;
+ }
+
+ /* Get the max list length we can send. Cap it at our max list length. */
+ BE_STREAM_TO_UINT16 (max_list_len, p_req);
+
+ if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN))
+ max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN;
+
+ p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq);
+
+ if ((!p_req) || (!attr_seq.num_attr))
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST);
+ return;
+ }
+
+ memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ;
+
+ /* Check if this is a continuation request */
+ if (*p_req)
+ {
+ if (*p_req++ != SDP_CONTINUATION_LEN)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN);
+ return;
+ }
+ BE_STREAM_TO_UINT16 (cont_offset, p_req);
+
+ if (cont_offset != p_ccb->cont_offset)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX);
+ return;
+ }
+
+ if (!p_ccb->rsp_list)
+ {
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
+ return;
+ }
+ is_cont = TRUE;
+
+ /* Initialise for continuation response */
+ p_rsp = &p_ccb->rsp_list[0];
+ attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id;
+ }
+ else
+ {
+ /* Get a scratch buffer to store response */
+ if (!p_ccb->rsp_list)
+ {
+ p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len);
+ if (p_ccb->rsp_list == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp");
+ return;
+ }
+ }
+
+ p_ccb->cont_offset = 0;
+ p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */
+
+ /* Reset continuation parameters in p_ccb */
+ p_ccb->cont_info.prev_sdp_rec = NULL;
+ p_ccb->cont_info.next_attr_index = 0;
+ p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
+ p_ccb->cont_info.attr_offset = 0;
+ }
+
+ /* Get a list of handles that match the UUIDs given to us */
+ for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq))
+ {
+ /* Allow space for attribute sequence type and length */
+ p_seq_start = p_rsp;
+ if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
+ {
+ /* See if there is enough room to include a new service in the current response */
+ rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
+ if (rem_len < 3)
+ {
+ /* Not enough room. Update continuation info for next response */
+ p_ccb->cont_info.next_attr_index = 0;
+ p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start;
+ break;
+ }
+ p_rsp += 3;
+ }
+
+ /* Get a list of handles that match the UUIDs given to us */
+ for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++)
+ {
+ p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end);
+
+ if (p_attr)
+ {
+ /* Check if attribute fits. Assume 3-byte value type/length */
+ rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]);
+
+ /* just in case */
+ if (rem_len <= 0)
+ {
+ p_ccb->cont_info.next_attr_index = xx;
+ p_ccb->cont_info.next_attr_start_id = p_attr->id;
+ maxxed_out = TRUE;
+ break;
+ }
+
+ attr_len = sdpu_get_attrib_entry_len(p_attr);
+ /* if there is a partial attribute pending to be sent */
+ if (p_ccb->cont_info.attr_offset)
+ {
+ p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len,
+ &p_ccb->cont_info.attr_offset);
+
+ /* If the partial attrib could not been fully added yet */
+ if (p_ccb->cont_info.attr_offset != attr_len)
+ {
+ maxxed_out = TRUE;
+ break;
+ }
+ else /* If the partial attrib has been added in full by now */
+ p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */
+ }
+ else if (rem_len < attr_len) /* Not enough space for attr... so add partially */
+ {
+ if (attr_len >= MAX_ATTR_LEN)
+ {
+ SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len);
+ sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL);
+ return;
+ }
+
+ /* add the partial attribute if possible */
+ p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len,
+ &p_ccb->cont_info.attr_offset);
+
+ p_ccb->cont_info.next_attr_index = xx;
+ p_ccb->cont_info.next_attr_start_id = p_attr->id;
+ maxxed_out = TRUE;
+ break;
+ }
+ else /* build the whole attribute */
+ p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr);
+
+ /* If doing a range, stick with this one till no more attributes found */
+ if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end)
+ {
+ /* Update for next time through */
+ attr_seq.attr_entry[xx].start = p_attr->id + 1;
+
+ xx--;
+ }
+ }
+ }
+
+ /* Go back and put the type and length into the buffer */
+ if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE)
+ {
+ seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav);
+ if (seq_len != 0)
+ {
+ UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
+ UINT16_TO_BE_STREAM (p_seq_start, seq_len);
+
+ if (maxxed_out)
+ p_ccb->cont_info.last_attr_seq_desc_sent = TRUE;
+ }
+ else
+ p_rsp = p_seq_start;
+ }
+
+ if (maxxed_out)
+ break;
+
+ /* Restore the attr_seq to look for in the next sdp record */
+ memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ;
+
+ /* Reset the next attr index */
+ p_ccb->cont_info.next_attr_index = 0;
+ p_ccb->cont_info.prev_sdp_rec = p_rec;
+ p_ccb->cont_info.last_attr_seq_desc_sent = FALSE;
+ }
+
+ /* response length */
+ len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]);
+ cont_offset = 0;
+
+ /* If first response, insert sequence header */
+ if (!is_cont)
+ {
+ /* Get the total list length for requested uid and attribute sequence */
+ p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3;
+ /* Put in the sequence header (2 or 3 bytes) */
+ if (p_ccb->list_len > 255)
+ {
+ p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
+ p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8);
+ p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
+ }
+ else
+ {
+ cont_offset = 1;
+
+ p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3);
+
+ p_ccb->list_len--;
+ len_to_send--;
+ }
+ }
+
+ /* Get a buffer to use to build the response */
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no buf for search rsp");
+ return;
+ }
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
+
+ /* Start building a rsponse */
+ UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP);
+ UINT16_TO_BE_STREAM (p_rsp, trans_num);
+
+ /* Skip the parameter length, add it when we know the length */
+ p_rsp_param_len = p_rsp;
+ p_rsp += 2;
+
+ /* Stream the list length to send */
+ UINT16_TO_BE_STREAM (p_rsp, len_to_send);
+
+ /* copy from rsp_list to the actual buffer to be sent */
+ memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send);
+ p_rsp += len_to_send;
+
+ p_ccb->cont_offset += len_to_send;
+
+ /* If anything left to send, continuation needed */
+ if (p_ccb->cont_offset < p_ccb->list_len)
+ {
+ is_cont = TRUE;
+
+ UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN);
+ UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset);
+ }
+ else
+ UINT8_TO_BE_STREAM (p_rsp, 0);
+
+ /* Go back and put the parameter length into the buffer */
+ rsp_param_len = p_rsp - p_rsp_param_len - 2;
+ UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_buf->len = p_rsp - p_rsp_start;
+
+
+ /* Send the buffer through L2CAP */
+ L2CA_DataWrite (p_ccb->connection_id, p_buf);
+}
+
+#endif /* SDP_SERVER_ENABLED == TRUE */
diff --git a/stack/sdp/sdp_utils.c b/stack/sdp/sdp_utils.c
new file mode 100644
index 0000000..cda0570
--- /dev/null
+++ b/stack/sdp/sdp_utils.c
@@ -0,0 +1,1040 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains SDP utility functions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "gki.h"
+#include "bt_types.h"
+
+#include "l2cdefs.h"
+#include "hcidefs.h"
+#include "hcimsgs.h"
+
+#include "sdp_api.h"
+#include "sdpint.h"
+
+#include "btu.h"
+
+
+static const UINT8 sdp_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+
+/*******************************************************************************
+**
+** Function sdpu_find_ccb_by_cid
+**
+** Description This function searches the CCB table for an entry with the
+** passed CID.
+**
+** Returns the CCB address, or NULL if not found.
+**
+*******************************************************************************/
+tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid)
+{
+ UINT16 xx;
+ tCONN_CB *p_ccb;
+
+ /* Look through each connection control block */
+ for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->connection_id == cid))
+ return (p_ccb);
+ }
+
+ /* If here, not found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_find_ccb_by_db
+**
+** Description This function searches the CCB table for an entry with the
+** passed discovery db.
+**
+** Returns the CCB address, or NULL if not found.
+**
+*******************************************************************************/
+tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db)
+{
+#if SDP_CLIENT_ENABLED == TRUE
+ UINT16 xx;
+ tCONN_CB *p_ccb;
+
+ if (p_db)
+ {
+ /* Look through each connection control block */
+ for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->p_db == p_db))
+ return (p_ccb);
+ }
+ }
+#endif
+ /* If here, not found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_allocate_ccb
+**
+** Description This function allocates a new CCB.
+**
+** Returns CCB address, or NULL if none available.
+**
+*******************************************************************************/
+tCONN_CB *sdpu_allocate_ccb (void)
+{
+ UINT16 xx;
+ tCONN_CB *p_ccb;
+
+ /* Look through each connection control block for a free one */
+ for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if (p_ccb->con_state == SDP_STATE_IDLE)
+ {
+ memset (p_ccb, 0, sizeof (tCONN_CB));
+
+ p_ccb->timer_entry.param = (UINT32) p_ccb;
+
+ return (p_ccb);
+ }
+ }
+
+ /* If here, no free CCB found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_release_ccb
+**
+** Description This function releases a CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdpu_release_ccb (tCONN_CB *p_ccb)
+{
+ /* Ensure timer is stopped */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ /* Drop any response pointer we may be holding */
+ p_ccb->con_state = SDP_STATE_IDLE;
+#if SDP_CLIENT_ENABLED == TRUE
+ p_ccb->is_attr_search = FALSE;
+#endif
+
+ /* Free the response buffer */
+ if (p_ccb->rsp_list)
+ {
+ SDP_TRACE_DEBUG0("releasing SDP rsp_list");
+
+ GKI_freebuf(p_ccb->rsp_list);
+ p_ccb->rsp_list = NULL;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_build_attrib_seq
+**
+** Description This function builds an attribute sequence from the list of
+** passed attributes. It is also passed the address of the output
+** buffer.
+**
+** Returns Pointer to next byte in the output buffer.
+**
+*******************************************************************************/
+UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs)
+{
+ UINT16 xx;
+
+ /* First thing is the data element header. See if the length fits 1 byte */
+ /* If no attributes, assume a 4-byte wildcard */
+ if (!p_attr)
+ xx = 5;
+ else
+ xx = num_attrs * 3;
+
+ if (xx > 255)
+ {
+ UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD);
+ UINT16_TO_BE_STREAM (p_out, xx);
+ }
+ else
+ {
+ UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
+ UINT8_TO_BE_STREAM (p_out, xx);
+ }
+
+ /* If there are no attributes specified, assume caller wants wildcard */
+ if (!p_attr)
+ {
+ UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_FOUR_BYTES);
+ UINT16_TO_BE_STREAM (p_out, 0);
+ UINT16_TO_BE_STREAM (p_out, 0xFFFF);
+ }
+ else
+ {
+ /* Loop through and put in all the attributes(s) */
+ for (xx = 0; xx < num_attrs; xx++, p_attr++)
+ {
+ UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p_out, *p_attr);
+ }
+ }
+
+ return (p_out);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_build_attrib_entry
+**
+** Description This function builds an attribute entry from the passed
+** attribute record. It is also passed the address of the output
+** buffer.
+**
+** Returns Pointer to next byte in the output buffer.
+**
+*******************************************************************************/
+UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr)
+{
+ /* First, store the attribute ID. Goes as a UINT */
+ UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
+ UINT16_TO_BE_STREAM (p_out, p_attr->id);
+
+ /* the attribute is in the db record.
+ * assuming the attribute len is less than SDP_MAX_ATTR_LEN */
+ switch(p_attr->type)
+ {
+ case TEXT_STR_DESC_TYPE: /* 4 */
+ case DATA_ELE_SEQ_DESC_TYPE:/* 6 */
+ case DATA_ELE_ALT_DESC_TYPE:/* 7 */
+ case URL_DESC_TYPE: /* 8 */
+#if (SDP_MAX_ATTR_LEN > 0xFFFF)
+ if(p_attr->len > 0xFFFF)
+ {
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_LONG);
+ UINT32_TO_BE_STREAM (p_out, p_attr->len);
+ }
+ else
+
+#endif /* 0xFFFF - 0xFF */
+#if (SDP_MAX_ATTR_LEN > 0xFF)
+ if(p_attr->len > 0xFF)
+ {
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_WORD);
+ UINT16_TO_BE_STREAM (p_out, p_attr->len);
+ }
+ else
+
+#endif /* 0xFF and less*/
+ {
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE);
+ UINT8_TO_BE_STREAM (p_out, p_attr->len);
+ }
+
+ ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len);
+
+ return (p_out);
+ }
+
+ /* Now, store the attribute value */
+ switch (p_attr->len)
+ {
+ case 1:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_ONE_BYTE);
+ break;
+ case 2:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_TWO_BYTES);
+ break;
+ case 4:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_FOUR_BYTES);
+ break;
+ case 8:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_EIGHT_BYTES);
+ break;
+ case 16:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_SIXTEEN_BYTES);
+ break;
+ default:
+ UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE);
+ UINT8_TO_BE_STREAM (p_out, p_attr->len);
+ break;
+ }
+
+ ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len);
+
+ return (p_out);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_build_n_send_error
+**
+** Description This function builds and sends an error packet.
+**
+** Returns void
+**
+*******************************************************************************/
+void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text)
+{
+ UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len;
+ UINT16 rsp_param_len;
+ BT_HDR *p_buf;
+
+
+ SDP_TRACE_WARNING2 ("SDP - sdpu_build_n_send_error code: 0x%x CID: 0x%x",
+ error_code, p_ccb->connection_id);
+
+ /* Get a buffer to use to build and send the packet to L2CAP */
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL)
+ {
+ SDP_TRACE_ERROR0 ("SDP - no buf for err msg");
+ return;
+ }
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
+
+ UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_ERROR_RESPONSE);
+ UINT16_TO_BE_STREAM (p_rsp, trans_num);
+
+ /* Skip the parameter length, we need to add it at the end */
+ p_rsp_param_len = p_rsp;
+ p_rsp += 2;
+
+ UINT16_TO_BE_STREAM (p_rsp, error_code);
+
+ /* Unplugfest example traces do not have any error text */
+ if (p_error_text)
+ ARRAY_TO_BE_STREAM (p_rsp, p_error_text, (int) strlen (p_error_text));
+
+ /* Go back and put the parameter length into the buffer */
+ rsp_param_len = p_rsp - p_rsp_param_len - 2;
+ UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len);
+
+ /* Set the length of the SDP data in the buffer */
+ p_buf->len = p_rsp - p_rsp_start;
+
+
+ /* Send the buffer through L2CAP */
+ L2CA_DataWrite (p_ccb->connection_id, p_buf);
+}
+
+
+
+/*******************************************************************************
+**
+** Function sdpu_extract_uid_seq
+**
+** Description This function extracts a UUID sequence from the passed input
+** buffer, and puts it into the passed output list.
+**
+** Returns Pointer to next byte in the input buffer after the sequence.
+**
+*******************************************************************************/
+UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq)
+{
+ UINT8 *p_seq_end;
+ UINT8 descr, type, size;
+ UINT32 seq_len, uuid_len;
+
+ /* Assume none found */
+ p_seq->num_uids = 0;
+
+ /* A UID sequence is composed of a bunch of UIDs. */
+
+ BE_STREAM_TO_UINT8 (descr, p);
+ type = descr >> 3;
+ size = descr & 7;
+
+ if (type != DATA_ELE_SEQ_DESC_TYPE)
+ return (NULL);
+
+ switch (size)
+ {
+ case SIZE_TWO_BYTES:
+ seq_len = 2;
+ break;
+ case SIZE_FOUR_BYTES:
+ seq_len = 4;
+ break;
+ case SIZE_SIXTEEN_BYTES:
+ seq_len = 16;
+ break;
+ case SIZE_IN_NEXT_BYTE:
+ BE_STREAM_TO_UINT8 (seq_len, p);
+ break;
+ case SIZE_IN_NEXT_WORD:
+ BE_STREAM_TO_UINT16 (seq_len, p);
+ break;
+ case SIZE_IN_NEXT_LONG:
+ BE_STREAM_TO_UINT32 (seq_len, p);
+ break;
+ default:
+ return (NULL);
+ }
+
+ if (seq_len >= param_len)
+ return (NULL);
+
+ p_seq_end = p + seq_len;
+
+ /* Loop through, extracting the UIDs */
+ for ( ; p < p_seq_end ; )
+ {
+ BE_STREAM_TO_UINT8 (descr, p);
+ type = descr >> 3;
+ size = descr & 7;
+
+ if (type != UUID_DESC_TYPE)
+ return (NULL);
+
+ switch (size)
+ {
+ case SIZE_TWO_BYTES:
+ uuid_len = 2;
+ break;
+ case SIZE_FOUR_BYTES:
+ uuid_len = 4;
+ break;
+ case SIZE_SIXTEEN_BYTES:
+ uuid_len = 16;
+ break;
+ case SIZE_IN_NEXT_BYTE:
+ BE_STREAM_TO_UINT8 (uuid_len, p);
+ break;
+ case SIZE_IN_NEXT_WORD:
+ BE_STREAM_TO_UINT16 (uuid_len, p);
+ break;
+ case SIZE_IN_NEXT_LONG:
+ BE_STREAM_TO_UINT32 (uuid_len, p);
+ break;
+ default:
+ return (NULL);
+ }
+
+ /* If UUID length is valid, copy it across */
+ if ((uuid_len == 2) || (uuid_len == 4) || (uuid_len == 16))
+ {
+ p_seq->uuid_entry[p_seq->num_uids].len = (UINT16) uuid_len;
+ BE_STREAM_TO_ARRAY (p, p_seq->uuid_entry[p_seq->num_uids].value, (int)uuid_len);
+ p_seq->num_uids++;
+ }
+ else
+ return (NULL);
+
+ /* We can only do so many */
+ if (p_seq->num_uids >= MAX_UUIDS_PER_SEQ)
+ return (NULL);
+ }
+
+ if (p != p_seq_end)
+ return (NULL);
+
+ return (p);
+}
+
+
+
+/*******************************************************************************
+**
+** Function sdpu_extract_attr_seq
+**
+** Description This function extracts an attribute sequence from the passed
+** input buffer, and puts it into the passed output list.
+**
+** Returns Pointer to next byte in the input buffer after the sequence.
+**
+*******************************************************************************/
+UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq)
+{
+ UINT8 *p_end_list;
+ UINT8 descr, type, size;
+ UINT32 list_len, attr_len;
+
+ /* Assume none found */
+ p_seq->num_attr = 0;
+
+ /* Get attribute sequence info */
+ BE_STREAM_TO_UINT8 (descr, p);
+ type = descr >> 3;
+ size = descr & 7;
+
+ if (type != DATA_ELE_SEQ_DESC_TYPE)
+ return (p);
+
+ switch (size)
+ {
+ case SIZE_IN_NEXT_BYTE:
+ BE_STREAM_TO_UINT8 (list_len, p);
+ break;
+
+ case SIZE_IN_NEXT_WORD:
+ BE_STREAM_TO_UINT16 (list_len, p);
+ break;
+
+ case SIZE_IN_NEXT_LONG:
+ BE_STREAM_TO_UINT32 (list_len, p);
+ break;
+
+ default:
+ return (p);
+ }
+
+ if (list_len > param_len)
+ return (p);
+
+ p_end_list = p + list_len;
+
+ /* Loop through, extracting the attribute IDs */
+ for ( ; p < p_end_list ; )
+ {
+ BE_STREAM_TO_UINT8 (descr, p);
+ type = descr >> 3;
+ size = descr & 7;
+
+ if (type != UINT_DESC_TYPE)
+ return (p);
+
+ switch (size)
+ {
+ case SIZE_TWO_BYTES:
+ attr_len = 2;
+ break;
+ case SIZE_FOUR_BYTES:
+ attr_len = 4;
+ break;
+ case SIZE_IN_NEXT_BYTE:
+ BE_STREAM_TO_UINT8 (attr_len, p);
+ break;
+ case SIZE_IN_NEXT_WORD:
+ BE_STREAM_TO_UINT16 (attr_len, p);
+ break;
+ case SIZE_IN_NEXT_LONG:
+ BE_STREAM_TO_UINT32 (attr_len, p);
+ break;
+ default:
+ return (NULL);
+ break;
+ }
+
+ /* Attribute length must be 2-bytes or 4-bytes for a paired entry. */
+ if (attr_len == 2)
+ {
+ BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p);
+ p_seq->attr_entry[p_seq->num_attr].end = p_seq->attr_entry[p_seq->num_attr].start;
+ }
+ else if (attr_len == 4)
+ {
+ BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p);
+ BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].end, p);
+ }
+ else
+ return (NULL);
+
+ /* We can only do so many */
+ if (++p_seq->num_attr >= MAX_ATTR_PER_SEQ)
+ return (NULL);
+ }
+
+ return (p);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_get_len_from_type
+**
+** Description This function gets the length
+**
+** Returns void
+**
+*******************************************************************************/
+UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len)
+{
+ UINT8 u8;
+ UINT16 u16;
+ UINT32 u32;
+
+ switch (type & 7)
+ {
+ case SIZE_ONE_BYTE:
+ *p_len = 1;
+ break;
+ case SIZE_TWO_BYTES:
+ *p_len = 2;
+ break;
+ case SIZE_FOUR_BYTES:
+ *p_len = 4;
+ break;
+ case SIZE_EIGHT_BYTES:
+ *p_len = 8;
+ break;
+ case SIZE_SIXTEEN_BYTES:
+ *p_len = 16;
+ break;
+ case SIZE_IN_NEXT_BYTE:
+ BE_STREAM_TO_UINT8 (u8, p);
+ *p_len = u8;
+ break;
+ case SIZE_IN_NEXT_WORD:
+ BE_STREAM_TO_UINT16 (u16, p);
+ *p_len = u16;
+ break;
+ case SIZE_IN_NEXT_LONG:
+ BE_STREAM_TO_UINT32 (u32, p);
+ *p_len = (UINT16) u32;
+ break;
+ }
+
+ return (p);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_is_base_uuid
+**
+** Description This function checks a 128-bit UUID with the base to see if
+** it matches. Only the last 12 bytes are compared.
+**
+** Returns TRUE if matched, else FALSE
+**
+*******************************************************************************/
+BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid)
+{
+ UINT16 xx;
+
+ for (xx = 4; xx < MAX_UUID_SIZE; xx++)
+ if (p_uuid[xx] != sdp_base_uuid[xx])
+ return (FALSE);
+
+ /* If here, matched */
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_compare_uuid_arrays
+**
+** Description This function compares 2 BE UUIDs. If needed, they are expanded
+** to 128-bit UUIDs, then compared.
+**
+** NOTE it is assumed that the arrays are in Big Endian format
+**
+** Returns TRUE if matched, else FALSE
+**
+*******************************************************************************/
+BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2)
+{
+ UINT8 nu1[MAX_UUID_SIZE];
+ UINT8 nu2[MAX_UUID_SIZE];
+
+ /* If lengths match, do a straight compare */
+ if (len1 == len2)
+ {
+ if (len1 == 2)
+ return ((p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1]));
+ if (len1 == 4)
+ return ( (p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1])
+ && (p_uuid1[2] == p_uuid2[2]) && (p_uuid1[3] == p_uuid2[3]) );
+ else
+ return (memcmp (p_uuid1, p_uuid2, (size_t)len1) == 0);
+ }
+ else if (len1 > len2)
+ {
+ /* If the len1 was 4-byte, (so len2 is 2-byte), compare on the fly */
+ if (len1 == 4)
+ {
+ return ( (p_uuid1[0] == 0) && (p_uuid1[1] == 0)
+ && (p_uuid1[2] == p_uuid2[0]) && (p_uuid1[3] == p_uuid2[1]) );
+ }
+ else
+ {
+ /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */
+ memcpy (nu1, p_uuid1, MAX_UUID_SIZE);
+ memcpy (nu2, sdp_base_uuid, MAX_UUID_SIZE);
+
+ if (len2 == 4)
+ memcpy (nu2, p_uuid2, len2);
+ else
+ memcpy (nu2 + 2, p_uuid2, len2);
+
+ return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0);
+ }
+ }
+ else
+ {
+ /* len2 is greater than len1 */
+ /* If the len2 was 4-byte, (so len1 is 2-byte), compare on the fly */
+ if (len2 == 4)
+ {
+ return ( (p_uuid2[0] == 0) && (p_uuid2[1] == 0)
+ && (p_uuid2[2] == p_uuid1[0]) && (p_uuid2[3] == p_uuid1[1]) );
+ }
+ else
+ {
+ /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */
+ memcpy (nu2, p_uuid2, MAX_UUID_SIZE);
+ memcpy (nu1, sdp_base_uuid, MAX_UUID_SIZE);
+
+ if (len1 == 4)
+ memcpy (nu1, p_uuid1, (size_t)len1);
+ else
+ memcpy (nu1 + 2, p_uuid1, (size_t)len1);
+
+ return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_compare_bt_uuids
+**
+** Description This function compares 2 BT UUID structures.
+**
+** NOTE it is assumed that BT UUID structures are compressed to the
+** smallest possible UUIDs (by removing the base SDP UUID)
+**
+** Returns TRUE if matched, else FALSE
+**
+*******************************************************************************/
+BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2)
+{
+ /* Lengths must match for BT UUIDs to match */
+ if (p_uuid1->len == p_uuid2->len)
+ {
+ if (p_uuid1->len == 2)
+ return (p_uuid1->uu.uuid16 == p_uuid2->uu.uuid16);
+ else if (p_uuid1->len == 4)
+ return (p_uuid1->uu.uuid32 == p_uuid2->uu.uuid32);
+ else if (!memcmp (p_uuid1->uu.uuid128, p_uuid2->uu.uuid128, 16))
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_compare_uuid_with_attr
+**
+** Description This function compares a BT UUID structure with the UUID in an
+** SDP attribute record. If needed, they are expanded to 128-bit
+** UUIDs, then compared.
+**
+** NOTE - it is assumed that BT UUID structures are compressed to the
+** smallest possible UUIDs (by removing the base SDP UUID).
+** - it is also assumed that the discovery atribute is compressed
+** to the smallest possible
+**
+** Returns TRUE if matched, else FALSE
+**
+*******************************************************************************/
+BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr)
+{
+ UINT16 attr_len = SDP_DISC_ATTR_LEN (p_attr->attr_len_type);
+
+ /* Since both UUIDs are compressed, lengths must match */
+ if (p_btuuid->len != attr_len)
+ return (FALSE);
+
+ if (p_btuuid->len == 2)
+ return (BOOLEAN)(p_btuuid->uu.uuid16 == p_attr->attr_value.v.u16);
+ else if (p_btuuid->len == 4)
+ return (BOOLEAN)(p_btuuid->uu.uuid32 == p_attr->attr_value.v.u32);
+ /* coverity[overrun-buffer-arg] */
+ /*
+ Event overrun-buffer-arg: Overrun of static array "&p_attr->attr_value.v.array" of size 4 bytes by passing it to a function which indexes it with argument "16U" at byte position 15
+ FALSE-POSITIVE error from Coverity test tool. Please do NOT remove following comment.
+ False-positive: SDP uses scratch buffer to hold the attribute value.
+ The actual size of tSDP_DISC_ATVAL does not matter.
+ If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily
+ */
+ else if (!memcmp (p_btuuid->uu.uuid128, p_attr->attr_value.v.array, MAX_UUID_SIZE))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function sdpu_sort_attr_list
+**
+** Description sorts a list of attributes in numeric order from lowest to
+** highest to conform to SDP specification
+**
+** Returns void
+**
+*******************************************************************************/
+void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db )
+{
+ UINT16 i;
+ UINT16 x;
+
+ /* Done if no attributes to sort */
+ if (num_attr <= 1)
+ {
+ return;
+ }
+ else if (num_attr > SDP_MAX_ATTR_FILTERS)
+ {
+ num_attr = SDP_MAX_ATTR_FILTERS;
+ }
+
+ num_attr--; /* for the for-loop */
+ for( i = 0; i < num_attr; )
+ {
+ if( p_db->attr_filters[i] > p_db->attr_filters[i+1] )
+ {
+ /* swap the attribute IDs and start from the beginning */
+ x = p_db->attr_filters[i];
+ p_db->attr_filters[i] = p_db->attr_filters[i+1];
+ p_db->attr_filters[i+1] = x;
+
+ i = 0;
+ }
+ else
+ i++;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_get_list_len
+**
+** Description gets the total list length in the sdp database for a given
+** uid sequence and attr sequence
+**
+** Returns void
+**
+*******************************************************************************/
+UINT16 sdpu_get_list_len(tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq)
+{
+ tSDP_RECORD *p_rec;
+ UINT16 len = 0;
+ UINT16 len1;
+
+ for (p_rec = sdp_db_service_search (NULL, uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, uid_seq))
+ {
+ len += 3;
+
+ len1 = sdpu_get_attrib_seq_len(p_rec, attr_seq );
+
+ if (len1 != 0)
+ len += len1;
+ else
+ len -= 3;
+ }
+ return len;
+}
+
+/*******************************************************************************
+**
+** Function sdpu_get_attrib_seq_len
+**
+** Description gets the length of the specific attributes in a given
+** sdp record
+**
+** Returns void
+**
+*******************************************************************************/
+UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq)
+{
+ tSDP_ATTRIBUTE *p_attr;
+ UINT16 len1 = 0;
+ UINT16 xx;
+ BOOLEAN is_range = FALSE;
+ UINT16 start_id, end_id;
+
+ for (xx = 0; xx < attr_seq->num_attr; xx++)
+ {
+ if (is_range == FALSE)
+ {
+ start_id = attr_seq->attr_entry[xx].start;
+ end_id = attr_seq->attr_entry[xx].end;
+ }
+ p_attr = sdp_db_find_attr_in_rec (p_rec,
+ start_id,
+ end_id);
+ if (p_attr)
+ {
+ len1 += sdpu_get_attrib_entry_len (p_attr);
+
+ /* If doing a range, stick with this one till no more attributes found */
+ if (start_id != end_id)
+ {
+ /* Update for next time through */
+ start_id = p_attr->id + 1;
+ xx--;
+ is_range = TRUE;
+ }
+ else
+ is_range = FALSE;
+ }
+ else
+ is_range = FALSE;
+ }
+ return len1;
+}
+
+/*******************************************************************************
+**
+** Function sdpu_get_attrib_entry_len
+**
+** Description gets the length of a specific attribute
+**
+** Returns void
+**
+*******************************************************************************/
+UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr)
+{
+ UINT16 len = 3;
+
+ /* the attribute is in the db record.
+ * assuming the attribute len is less than SDP_MAX_ATTR_LEN */
+ switch(p_attr->type)
+ {
+ case TEXT_STR_DESC_TYPE: /* 4 */
+ case DATA_ELE_SEQ_DESC_TYPE:/* 6 */
+ case DATA_ELE_ALT_DESC_TYPE:/* 7 */
+ case URL_DESC_TYPE: /* 8 */
+#if (SDP_MAX_ATTR_LEN > 0xFFFF)
+ if(p_attr->len > 0xFFFF)
+ {
+ len += 5;
+ }
+ else
+
+#endif/* 0xFFFF - 0xFF */
+#if (SDP_MAX_ATTR_LEN > 0xFF)
+ if(p_attr->len > 0xFF)
+ {
+ len += 3;
+ }
+ else
+
+#endif /* 0xFF and less*/
+ {
+ len += 2;
+ }
+ len += p_attr->len;
+ return len;
+ }
+
+ /* Now, the attribute value */
+ switch (p_attr->len)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ len += 1;
+ break;
+ default:
+ len += 2;
+ break;
+ }
+
+ len += p_attr->len;
+ return len;
+}
+
+
+/*******************************************************************************
+**
+** Function sdpu_build_partial_attrib_entry
+**
+** Description This function fills a buffer with partial attribute. It is
+** assumed that the maximum size of any attribute is 256 bytes.
+**
+** p_out: output buffer
+** p_attr: attribute to be copied partially into p_out
+** rem_len: num bytes to copy into p_out
+** offset: current start offset within the attr that needs to be copied
+**
+** Returns Pointer to next byte in the output buffer.
+** offset is also updated
+**
+*******************************************************************************/
+UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset)
+{
+ UINT8 tmp_attr[MAX_ATTR_LEN];
+ UINT8 *p_tmp_attr = &tmp_attr[0];
+ size_t len_to_copy;
+ UINT16 attr_len;
+
+ sdpu_build_attrib_entry(p_tmp_attr, p_attr);
+ attr_len = sdpu_get_attrib_entry_len(p_attr);
+
+ len_to_copy = ((attr_len - *offset) < len) ? (attr_len - *offset): len;
+
+ memcpy(p_out, &tmp_attr[*offset], len_to_copy);
+
+ p_out = &p_out[len_to_copy];
+ *offset += len_to_copy;
+
+ return p_out;
+}
+
+/*******************************************************************************
+**
+** Function sdpu_uuid16_to_uuid128
+**
+** Description This function converts UUID-16 to UUID-128 by including the base UUID
+**
+** uuid16: 2-byte UUID
+** p_uuid128: Expanded 128-bit UUID
+**
+** Returns None
+**
+*******************************************************************************/
+void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8* p_uuid128)
+{
+ UINT16 uuid16_bo;
+ memset(p_uuid128, 0, 16);
+
+ memcpy(p_uuid128, sdp_base_uuid, MAX_UUID_SIZE);
+ uuid16_bo = ntohs(uuid16);
+ memcpy(p_uuid128+ 2, &uuid16_bo, sizeof(uint16_t));
+}
diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h
new file mode 100644
index 0000000..b300664
--- /dev/null
+++ b/stack/sdp/sdpint.h
@@ -0,0 +1,329 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains internally used SDP definitions
+ *
+ ******************************************************************************/
+
+#ifndef SDP_INT_H
+#define SDP_INT_H
+
+#include "bt_target.h"
+#include "sdp_api.h"
+#include "l2c_api.h"
+
+
+/* Continuation length - we use a 2-byte offset */
+#define SDP_CONTINUATION_LEN 2
+#define SDP_MAX_CONTINUATION_LEN 16 /* As per the spec */
+
+/* Timeout definitions. */
+#define SDP_INACT_TIMEOUT 30 /* Inactivity timeout */
+
+
+/* Define the Out-Flow default values. */
+#define SDP_OFLOW_QOS_FLAG 0
+#define SDP_OFLOW_SERV_TYPE 0
+#define SDP_OFLOW_TOKEN_RATE 0
+#define SDP_OFLOW_TOKEN_BUCKET_SIZE 0
+#define SDP_OFLOW_PEAK_BANDWIDTH 0
+#define SDP_OFLOW_LATENCY 0
+#define SDP_OFLOW_DELAY_VARIATION 0
+
+/* Define the In-Flow default values. */
+#define SDP_IFLOW_QOS_FLAG 0
+#define SDP_IFLOW_SERV_TYPE 0
+#define SDP_IFLOW_TOKEN_RATE 0
+#define SDP_IFLOW_TOKEN_BUCKET_SIZE 0
+#define SDP_IFLOW_PEAK_BANDWIDTH 0
+#define SDP_IFLOW_LATENCY 0
+#define SDP_IFLOW_DELAY_VARIATION 0
+
+#define SDP_LINK_TO 0
+
+/* Define the type of device notification. */
+/* (Inquiry Scan and Page Scan) */
+#define SDP_DEVICE_NOTI_LEN sizeof (BT_HDR) + \
+ HCIC_PREAMBLE_SIZE + \
+ HCIC_PARAM_SIZE_WRITE_PARAM1
+
+#define SDP_DEVICE_NOTI_FLAG 0x03
+
+/* Define the Protocol Data Unit (PDU) types.
+*/
+#define SDP_PDU_ERROR_RESPONSE 0x01
+#define SDP_PDU_SERVICE_SEARCH_REQ 0x02
+#define SDP_PDU_SERVICE_SEARCH_RSP 0x03
+#define SDP_PDU_SERVICE_ATTR_REQ 0x04
+#define SDP_PDU_SERVICE_ATTR_RSP 0x05
+#define SDP_PDU_SERVICE_SEARCH_ATTR_REQ 0x06
+#define SDP_PDU_SERVICE_SEARCH_ATTR_RSP 0x07
+
+/* Max UUIDs and attributes we support per sequence */
+#define MAX_UUIDS_PER_SEQ 16
+#define MAX_ATTR_PER_SEQ 16
+
+/* Max length we support for any attribute */
+// btla-specific ++
+#ifdef SDP_MAX_ATTR_LEN
+#define MAX_ATTR_LEN SDP_MAX_ATTR_LEN
+#else
+#define MAX_ATTR_LEN 256
+#endif
+// btla-specific --
+
+/* Internal UUID sequence representation */
+typedef struct
+{
+ UINT16 len;
+ UINT8 value[MAX_UUID_SIZE];
+} tUID_ENT;
+
+typedef struct
+{
+ UINT16 num_uids;
+ tUID_ENT uuid_entry[MAX_UUIDS_PER_SEQ];
+} tSDP_UUID_SEQ;
+
+
+/* Internal attribute sequence definitions */
+typedef struct
+{
+ UINT16 start;
+ UINT16 end;
+} tATT_ENT;
+
+typedef struct
+{
+ UINT16 num_attr;
+ tATT_ENT attr_entry[MAX_ATTR_PER_SEQ];
+} tSDP_ATTR_SEQ;
+
+
+/* Define the attribute element of the SDP database record */
+typedef struct
+{
+ UINT32 len; /* Number of bytes in the entry */
+ UINT8 *value_ptr; /* Points to attr_pad */
+ UINT16 id;
+ UINT8 type;
+} tSDP_ATTRIBUTE;
+
+/* An SDP record consists of a handle, and 1 or more attributes */
+typedef struct
+{
+ UINT32 record_handle;
+ UINT32 free_pad_ptr;
+ UINT16 num_attributes;
+ tSDP_ATTRIBUTE attribute[SDP_MAX_REC_ATTR];
+ UINT8 attr_pad[SDP_MAX_PAD_LEN];
+} tSDP_RECORD;
+
+
+/* Define the SDP database */
+typedef struct
+{
+ UINT32 di_primary_handle; /* Device ID Primary record or NULL if nonexistent */
+ BOOLEAN brcm_di_registered;
+ UINT16 num_records;
+ tSDP_RECORD record[SDP_MAX_RECORDS];
+} tSDP_DB;
+
+enum
+{
+ SDP_IS_SEARCH,
+ SDP_IS_ATTR_SEARCH,
+ SDP_IS_PASS_THRU /* only when SDP_FOR_JV_INCLUDED == TRUE */
+};
+
+#if SDP_SERVER_ENABLED == TRUE
+/* Continuation information for the SDP server response */
+typedef struct
+{
+ UINT16 next_attr_index; /* attr index for next continuation response */
+ UINT16 next_attr_start_id; /* attr id to start with for the attr index in next cont. response */
+ tSDP_RECORD *prev_sdp_rec; /* last sdp record that was completely sent in the response */
+ BOOLEAN last_attr_seq_desc_sent; /* whether attr seq length has been sent previously */
+ UINT16 attr_offset; /* offset within the attr to keep trak of partial attributes in the responses */
+} tSDP_CONT_INFO;
+#endif /* SDP_SERVER_ENABLED == TRUE */
+
+/* Define the SDP Connection Control Block */
+typedef struct
+{
+#define SDP_STATE_IDLE 0
+#define SDP_STATE_CONN_SETUP 1
+#define SDP_STATE_CFG_SETUP 2
+#define SDP_STATE_CONNECTED 3
+ UINT8 con_state;
+
+#define SDP_FLAGS_IS_ORIG 0x01
+#define SDP_FLAGS_HIS_CFG_DONE 0x02
+#define SDP_FLAGS_MY_CFG_DONE 0x04
+ UINT8 con_flags;
+
+ BD_ADDR device_address;
+ TIMER_LIST_ENT timer_entry;
+ UINT16 rem_mtu_size;
+ UINT16 connection_id;
+ UINT16 list_len; /* length of the response in the GKI buffer */
+ UINT8 *rsp_list; /* pointer to GKI buffer holding response */
+
+#if SDP_CLIENT_ENABLED == TRUE
+ tSDP_DISCOVERY_DB *p_db; /* Database to save info into */
+ tSDP_DISC_CMPL_CB *p_cb; /* Callback for discovery done */
+ tSDP_DISC_CMPL_CB2 *p_cb2; /* Callback for discovery done piggy back with the user data */
+ void *user_data; /* piggy back user data */
+ UINT32 handles[SDP_MAX_DISC_SERVER_RECS]; /* Discovered server record handles */
+ UINT16 num_handles; /* Number of server handles */
+ UINT16 cur_handle; /* Current handle being processed */
+ UINT16 transaction_id;
+ UINT16 disconnect_reason; /* Disconnect reason */
+#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
+ UINT16 cur_uuid_idx;
+#endif
+
+#define SDP_DISC_WAIT_CONN 0
+#define SDP_DISC_WAIT_HANDLES 1
+#define SDP_DISC_WAIT_ATTR 2
+#define SDP_DISC_WAIT_SEARCH_ATTR 3
+#define SDP_DISC_WAIT_PASS_THRU 4 /* only when SDP_FOR_JV_INCLUDED == TRUE */
+#define SDP_DISC_WAIT_CANCEL 5
+
+ UINT8 disc_state;
+ UINT8 is_attr_search;
+#endif /* SDP_CLIENT_ENABLED == TRUE */
+
+#if SDP_SERVER_ENABLED == TRUE
+ UINT16 cont_offset; /* Continuation state data in the server response */
+ tSDP_CONT_INFO cont_info; /* structure to hold continuation information for the server response */
+#endif /* SDP_SERVER_ENABLED == TRUE */
+
+} tCONN_CB;
+
+
+/* The main SDP control block */
+typedef struct
+{
+ tL2CAP_CFG_INFO l2cap_my_cfg; /* My L2CAP config */
+ tCONN_CB ccb[SDP_MAX_CONNECTIONS];
+#if SDP_SERVER_ENABLED == TRUE
+ tSDP_DB server_db;
+#endif
+ tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */
+ UINT16 max_attr_list_size; /* Max attribute list size to use */
+ UINT16 max_recs_per_search; /* Max records we want per seaarch */
+ UINT8 trace_level;
+} tSDP_CB;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Global SDP data */
+#if SDP_DYNAMIC_MEMORY == FALSE
+SDP_API extern tSDP_CB sdp_cb;
+#else
+SDP_API extern tSDP_CB *sdp_cb_ptr;
+#define sdp_cb (*sdp_cb_ptr)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Functions provided by sdp_main.c */
+SDP_API extern void sdp_init (void);
+extern void sdp_disconnect (tCONN_CB*p_ccb, UINT16 reason);
+
+#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE)
+SDP_API extern UINT16 sdp_set_max_attr_list_size (UINT16 max_size);
+#endif
+
+/* Functions provided by sdp_conn.c
+*/
+extern void sdp_conn_rcv_l2e_conn_ind (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_conn_cfm (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_disc (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_config_ind (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_config_cfm (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_connected (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg);
+extern void sdp_conn_rcv_l2e_data (BT_HDR *p_msg);
+extern void sdp_conn_timeout (tCONN_CB *p_ccb);
+
+extern tCONN_CB *sdp_conn_originate (UINT8 *p_bd_addr);
+
+/* Functions provided by sdp_utils.c
+*/
+extern tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid);
+extern tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db);
+extern tCONN_CB *sdpu_allocate_ccb (void);
+extern void sdpu_release_ccb (tCONN_CB *p_ccb);
+
+extern UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs);
+extern UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr);
+extern void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text);
+
+extern UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq);
+extern UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq);
+
+SDP_API extern UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len);
+extern BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid);
+extern BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2);
+SDP_API extern BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2);
+extern BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr);
+
+extern void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db );
+extern UINT16 sdpu_get_list_len( tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq );
+extern UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq);
+extern UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr);
+extern UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset);
+extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8* p_uuid128);
+
+/* Functions provided by sdp_db.c
+*/
+extern tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq);
+extern tSDP_RECORD *sdp_db_find_record (UINT32 handle);
+extern tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, UINT16 end_attr);
+
+
+/* Functions provided by sdp_server.c
+*/
+#if SDP_SERVER_ENABLED == TRUE
+extern void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg);
+#else
+#define sdp_server_handle_client_req(p_ccb, p_msg)
+#endif
+
+/* Functions provided by sdp_discovery.c
+*/
+#if SDP_CLIENT_ENABLED == TRUE
+extern void sdp_disc_connected (tCONN_CB *p_ccb);
+extern void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg);
+#else
+#define sdp_disc_connected(p_ccb)
+#define sdp_disc_server_rsp(p_ccb, p_msg)
+#endif
+
+
+
+#endif
+