diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/sdp | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/sdp')
-rw-r--r-- | stack/sdp/sdp_api.c | 1264 | ||||
-rw-r--r-- | stack/sdp/sdp_db.c | 948 | ||||
-rw-r--r-- | stack/sdp/sdp_discovery.c | 1113 | ||||
-rw-r--r-- | stack/sdp/sdp_main.c | 694 | ||||
-rw-r--r-- | stack/sdp/sdp_server.c | 821 | ||||
-rw-r--r-- | stack/sdp/sdp_utils.c | 1004 | ||||
-rw-r--r-- | stack/sdp/sdpint.h | 313 |
7 files changed, 6157 insertions, 0 deletions
diff --git a/stack/sdp/sdp_api.c b/stack/sdp/sdp_api.c new file mode 100644 index 0000000..2545181 --- /dev/null +++ b/stack/sdp/sdp_api.c @@ -0,0 +1,1264 @@ +/***************************************************************************** +** +** Name: sdp_api.c +** +** Description: this file contains SDP interface functions +** +** +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#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" + + +/********************************************************************** +** 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 +} + +#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_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) + /* 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) + /* 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_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) + { + 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..e9a35fc --- /dev/null +++ b/stack/sdp/sdp_db.c @@ -0,0 +1,948 @@ +/***************************************************************************** +** * +** Name: sdp_db.c * +** * +** Description: this file contains functions that handle the database * +** * +** Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. * +** WIDCOMM Bluetooth Core. Proprietary and confidential. * +******************************************************************************/ + +#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..1092584 --- /dev/null +++ b/stack/sdp/sdp_discovery.c @@ -0,0 +1,1113 @@ +/**************************************************************************** +** +** Name: sdp_discovery.c +** +** Description: this file contains SDP discovery functions +** +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#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..8a9f6c5 --- /dev/null +++ b/stack/sdp/sdp_main.c @@ -0,0 +1,694 @@ +/*****************************************************************************/ +/* */ +/* Name: sdp_main.c */ +/* */ +/* Description: this file contains the main SDP functions */ +/* */ +/* Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. */ +/* Broadcom Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#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) + { + 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)) + (*p_ccb->p_cb) (SDP_SECURITY_ERR); + else if (result == HCI_ERR_HOST_REJECT_DEVICE) + (*p_ccb->p_cb) (SDP_CONN_REJECTED); + else + (*p_ccb->p_cb) (SDP_CONN_FAILED); + } + + 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)); +#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); + + 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); + + 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); +#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..3640dc2 --- /dev/null +++ b/stack/sdp/sdp_server.c @@ -0,0 +1,821 @@ +/*****************************************************************************/ +/* */ +/* Name: sdp_server.c */ +/* */ +/* Description: this file contains functions that handle the SDP server */ +/* functions. This is mainly dealing with client requests */ +/* */ +/* Copyright (c) 1999-2004, WIDCOMM Inc., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#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..2e0fdd8 --- /dev/null +++ b/stack/sdp/sdp_utils.c @@ -0,0 +1,1004 @@ +/*****************************************************************************/ +/* */ +/* Name: sdp_utils.c */ +/* */ +/* Description: this file contains SDP utility functions */ +/* */ +/* Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. */ +/* Broadcom Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#include <stdlib.h> +#include <string.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; +} + diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h new file mode 100644 index 0000000..0f16b8c --- /dev/null +++ b/stack/sdp/sdpint.h @@ -0,0 +1,313 @@ +/****************************************************************************/ +/* */ +/* Name: sdp_int.h */ +/* */ +/* Function this file contains internally used SDP definitions */ +/* */ +/* Copyright (c) 1999-2009, Broadcom Corp., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/* */ +/*****************************************************************************/ + +#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 */ + 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); + + +/* 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 + |