Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
@@ -0,0 +1,319 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains internally used SDP definitions
*
******************************************************************************/
#ifndef SDP_INT_H
#define SDP_INT_H
#include "common/bt_target.h"
#include "common/bt_defs.h"
#include "stack/sdp_api.h"
#include "stack/l2c_api.h"
#include "osi/list.h"
#if (SDP_INCLUDED == TRUE)
/* 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 */
#if BT_SDP_BQB_INCLUDED
#define SDP_BQB_INACT_TIMEOUT 90 /* Inactivity timeout for BQB test */
#endif /* BT_SDP_BQB_INCLUDED */
/* 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 8
#define MAX_ATTR_PER_SEQ 8
/* 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 */
UINT16 num_records;
list_t *p_record_list;
} tSDP_DB;
enum {
SDP_IS_SEARCH,
SDP_IS_ATTR_SEARCH,
};
#if SDP_SERVER_ENABLED == TRUE
/* Continuation information for the SDP server response */
typedef struct {
UINT16 next_attr_index; /* attr index for next continuation response */
UINT16 next_attr_start_id; /* attr id to start with for the attr index in next cont. response */
tSDP_RECORD *prev_sdp_rec; /* last sdp record that was completely sent in the response */
BOOLEAN last_attr_seq_desc_sent; /* whether attr seq length has been sent previously */
UINT16 attr_offset; /* offset within the attr to keep trak of partial attributes in the responses */
} tSDP_CONT_INFO;
#endif /* SDP_SERVER_ENABLED == TRUE */
/* Define the SDP Connection Control Block */
typedef struct {
#define SDP_STATE_IDLE 0
#define SDP_STATE_CONN_SETUP 1
#define SDP_STATE_CFG_SETUP 2
#define SDP_STATE_CONNECTED 3
UINT8 con_state;
#define SDP_FLAGS_IS_ORIG 0x01
#define SDP_FLAGS_HIS_CFG_DONE 0x02
#define SDP_FLAGS_MY_CFG_DONE 0x04
UINT8 con_flags;
BD_ADDR device_address;
TIMER_LIST_ENT timer_entry;
UINT16 rem_mtu_size;
UINT16 connection_id;
UINT16 list_len; /* length of the response in the GKI buffer */
UINT8 *rsp_list; /* pointer to GKI buffer holding response */
#if SDP_CLIENT_ENABLED == TRUE
tSDP_DISCOVERY_DB *p_db; /* Database to save info into */
tSDP_DISC_CMPL_CB *p_cb; /* Callback for discovery done */
tSDP_DISC_CMPL_CB2 *p_cb2; /* Callback for discovery done piggy back with the user data */
void *user_data; /* piggy back user data */
UINT32 handles[SDP_MAX_DISC_SERVER_RECS]; /* Discovered server record handles */
UINT16 num_handles; /* Number of server handles */
UINT16 cur_handle; /* Current handle being processed */
UINT16 transaction_id;
UINT16 disconnect_reason; /* Disconnect reason */
#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE)
UINT16 cur_uuid_idx;
#endif
#define SDP_DISC_WAIT_CONN 0
#define SDP_DISC_WAIT_HANDLES 1
#define SDP_DISC_WAIT_ATTR 2
#define SDP_DISC_WAIT_SEARCH_ATTR 3
#define SDP_DISC_WAIT_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
extern tSDP_CB sdp_cb;
#else
extern tSDP_CB *sdp_cb_ptr;
#define sdp_cb (*sdp_cb_ptr)
#endif
#ifdef __cplusplus
}
#endif
/* Functions provided by sdp_main.c */
extern void sdp_init (void);
extern void sdp_deinit (void);
extern void sdp_disconnect (tCONN_CB *p_ccb, UINT16 reason);
#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE)
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);
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);
extern BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2);
extern BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr);
extern void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db );
extern UINT16 sdpu_get_list_len( tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq );
extern UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq);
extern UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr);
extern UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset);
extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8 *p_uuid128);
/* Functions provided by sdp_db.c
*/
extern tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq);
extern tSDP_RECORD *sdp_db_find_record (UINT32 handle);
extern tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, UINT16 end_attr);
/* Functions provided by sdp_server.c
*/
#if SDP_SERVER_ENABLED == TRUE
extern void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg);
#else
#define sdp_server_handle_client_req(p_ccb, p_msg)
#endif
/* Functions provided by sdp_discovery.c
*/
#if SDP_CLIENT_ENABLED == TRUE
extern void sdp_disc_connected (tCONN_CB *p_ccb);
extern void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg);
#else
#define sdp_disc_connected(p_ccb)
#define sdp_disc_server_rsp(p_ccb, p_msg)
#endif
#endif ///SDP_INCLUDED == TRUE
#endif
File diff suppressed because it is too large Load Diff
+992
View File
@@ -0,0 +1,992 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* this file contains functions that handle the database
*
******************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "common/bt_target.h"
#include "osi/allocator.h"
#include "stack/l2cdefs.h"
#include "stack/hcidefs.h"
#include "stack/hcimsgs.h"
#include "stack/sdp_api.h"
#include "sdpint.h"
#include "osi/list.h"
#if (SDP_INCLUDED == TRUE)
#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;
list_node_t *p_node = NULL;
/* If NULL, start at the beginning, else start at the first specified record */
if (!p_rec) {
p_node = list_begin(sdp_cb.server_db.p_record_list);
} else {
/* get node in the record list with given p_rec */
p_node = list_get_node(sdp_cb.server_db.p_record_list, p_rec);
if (p_node == NULL) {
return NULL;
}
/* get next node */
p_node = list_next(p_node);
}
/* Look through the records. The spec says that a match occurs if */
/* the record contains all the passed UUIDs in it. */
for( ; p_node; p_node = list_next(p_node)) {
p_rec = list_node(p_node);
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;
list_node_t *p_node = NULL;
/* Look through the records for the caller's handle */
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
p_rec = list_node(p_node);
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;
tSDP_RECORD *p_rec = NULL;
tSDP_RECORD *p_rec_prev = NULL;
/* First, check if there is a free record */
if (p_db->num_records < SDP_MAX_RECORDS) {
p_rec =(tSDP_RECORD *)osi_malloc(sizeof(tSDP_RECORD));
if (p_rec) {
memset(p_rec, 0, sizeof(tSDP_RECORD));
/* Save previous rec */
if (p_db->num_records) {
p_rec_prev = list_back(p_db->p_record_list);
}
/* Append new record */
list_append(p_db->p_record_list, p_rec);
/* We will use a handle of the first unreserved handle plus last record
** number + 1 */
if (p_db->num_records) {
handle = p_rec_prev->record_handle + 1;
} else {
handle = 0x10000;
}
p_rec->record_handle = handle;
p_db->num_records++;
SDP_TRACE_DEBUG("SDP_CreateRecord ok, num_records:%d\n", 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_rec->record_handle);
} else {
SDP_TRACE_ERROR("SDP_CreateRecord fail, memory allocation failed\n");
}
} else {
SDP_TRACE_ERROR("SDP_CreateRecord fail, exceed maximum records:%d\n", SDP_MAX_RECORDS);
}
#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
tSDP_RECORD *p_rec = NULL;
list_node_t *p_node = NULL;
if (handle == 0 || sdp_cb.server_db.num_records == 0) {
/* Delete all records in the database */
sdp_cb.server_db.num_records = 0;
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
list_remove(sdp_cb.server_db.p_record_list, p_node);
}
/* require new DI record to be created in SDP_SetLocalDiRecord */
sdp_cb.server_db.di_primary_handle = 0;
return (TRUE);
} else {
/* Find the record in the database */
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
p_rec = list_node(p_node);
if (p_rec->record_handle == handle) {
/* Found it. Shift everything up one */
list_remove(sdp_cb.server_db.p_record_list, p_rec);
sdp_cb.server_db.num_records--;
SDP_TRACE_DEBUG("SDP_DeleteRecord ok, num_records:%d\n", 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;
}
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;
tSDP_RECORD *p_rec = NULL;
list_node_t *p_node= NULL;
#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_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s\n",
handle, attr_id, attr_type, attr_len, p_val, num_array);
} else if (attr_type == BOOLEAN_DESC_TYPE) {
SDP_TRACE_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%d\n",
handle, attr_id, attr_type, attr_len, p_val, *p_val);
} else {
SDP_TRACE_DEBUG("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s\n",
handle, attr_id, attr_type, attr_len, p_val, p_val);
}
}
#endif
/* Find the record in the database */
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
p_rec= list_node(p_node);
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_WARNING("SDP_AddAttribute: attr_len:%d too long. truncate to (%d)\n",
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_ERROR("SDP_AddAttribute fail, length exceed maximum: ID %d: attr_len:%d \n",
attr_id, attr_len );
p_attr->id = p_attr->type = p_attr->len = 0;
return (FALSE);
}
p_rec->num_attributes++;
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 *p_buff;
UINT8 *p;
UINT8 *p_head;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) {
SDP_TRACE_ERROR("SDP_AddSequence cannot get a buffer!\n");
return (FALSE);
}
p = 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 - p_buff > SDP_MAX_ATTR_LEN) {
/* go back to before we add this element */
p = p_head;
if (p_head == p_buff) {
/* the first element exceed the max length */
SDP_TRACE_ERROR ("SDP_AddSequence - too long(attribute is not added)!!\n");
osi_free(p_buff);
return FALSE;
} else {
SDP_TRACE_ERROR ("SDP_AddSequence - too long, add %d elements of %d\n", xx, num_elem);
}
break;
}
}
result = SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
UINT8 *p;
INT32 max_len = SDP_MAX_ATTR_LEN - 3;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) {
SDP_TRACE_ERROR("SDP_AddUuidSequence cannot get a buffer!\n");
return (FALSE);
}
p = p_buff;
/* 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 - p_buff) > max_len) {
SDP_TRACE_WARNING ("SDP_AddUuidSequence - too long, add %d uuids of %d\n", xx, num_uuids);
break;
}
}
result = SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
int offset;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) {
SDP_TRACE_ERROR("SDP_AddProtocolList cannot get a buffer!\n");
return (FALSE);
}
offset = sdp_compose_proto_list(p_buff, num_elem, p_elem_list);
result = SDP_AddAttribute (handle, ATTR_ID_PROTOCOL_DESC_LIST, DATA_ELE_SEQ_DESC_TYPE, (UINT32) offset, p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
UINT8 *p;
UINT8 *p_len;
int offset;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) {
SDP_TRACE_ERROR("SDP_AddAdditionProtoLists cannot get a buffer!\n");
return (FALSE);
}
p = p_buff;
/* 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);
}
result = SDP_AddAttribute (handle, ATTR_ID_ADDITION_PROTO_DESC_LISTS, DATA_ELE_SEQ_DESC_TYPE,
(UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
UINT8 *p;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN)) == NULL) {
SDP_TRACE_ERROR("SDP_AddProfileDescriptorList cannot get a buffer!\n");
return (FALSE);
}
p = 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 */
*p_buff = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
*(p_buff + 1) = (UINT8) (p - (p_buff + 2));
result = SDP_AddAttribute (handle, ATTR_ID_BT_PROFILE_DESC_LIST, DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
UINT8 *p;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN)) == NULL) {
SDP_TRACE_ERROR("SDP_AddLanguageBaseAttrIDList cannot get a buffer!\n");
return (FALSE);
}
p = 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);
result = SDP_AddAttribute (handle, ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST, DATA_ELE_SEQ_DESC_TYPE,
(UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 *p_buff;
UINT8 *p;
BOOLEAN result;
if ((p_buff = (UINT8 *) osi_malloc(sizeof(UINT8) * SDP_MAX_ATTR_LEN * 2)) == NULL) {
SDP_TRACE_ERROR("SDP_AddServiceClassIdList cannot get a buffer!\n");
return (FALSE);
}
p = 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);
}
result = SDP_AddAttribute (handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE,
(UINT32) (p - p_buff), p_buff);
osi_free(p_buff);
return result;
#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 = NULL;
list_node_t *p_node= NULL;
UINT8 *pad_ptr;
UINT32 len; /* Number of bytes in the entry */
/* Find the record in the database */
for(p_node = list_begin(sdp_cb.server_db.p_record_list); p_node; p_node = list_next(p_node)) {
p_rec= list_node(p_node);
if (p_rec->record_handle == handle) {
tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0];
SDP_TRACE_API("Deleting attr_id 0x%04x for handle 0x%x\n", 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 offset = -1; /* default to not found */
#if SDP_SERVER_ENABLED == TRUE
INT32 len = 0; /* Number of bytes in the entry */
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
#endif ///SDP_INCLUDED == TRUE
File diff suppressed because it is too large Load Diff
+786
View File
@@ -0,0 +1,786 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains the main SDP functions
*
******************************************************************************/
#include <stdlib.h>
#include <string.h>
//#include <stdio.h>
#include "common/bt_target.h"
#include "osi/allocator.h"
#include "stack/l2cdefs.h"
#include "stack/hcidefs.h"
#include "stack/hcimsgs.h"
#include "stack/l2c_api.h"
#include "stack/l2cdefs.h"
#include "stack/btu.h"
#include "stack/btm_api.h"
#include "stack/sdp_api.h"
#include "sdpint.h"
#include "osi/list.h"
#if (SDP_INCLUDED == TRUE)
/********************************************************************************/
/* G L O B A L S D P D A T A */
/********************************************************************************/
#if SDP_DYNAMIC_MEMORY == FALSE
tSDP_CB sdp_cb;
#else
tSDP_CB *sdp_cb_ptr;
#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
#if BT_SDP_BQB_INCLUDED
static BOOLEAN s_sdp_bqb_disable_flag = FALSE;
static BOOLEAN s_sdp_bqb_inact_timeout_flag = FALSE;
BOOLEAN l2cap_bqb_ertm_mode_included_flag = FALSE;
#endif /* BT_SDP_BQB_INCLUDED */
/*******************************************************************************
**
** Function sdp_bqb_disable_ctrl
**
** Description Control the disable of bqb for SDP BQB test
**
** Returns void
**
*******************************************************************************/
#if BT_SDP_BQB_INCLUDED
void sdp_bqb_disable_ctrl(BOOLEAN enable)
{
s_sdp_bqb_disable_flag = enable;
}
#endif /* BT_SDP_BQB_INCLUDED */
/*******************************************************************************
**
** Function sdp_bqb_inact_timeout_ctrl
**
** Description Control the inactivity timeout for SDP BQB test
**
** Returns void
**
*******************************************************************************/
#if BT_SDP_BQB_INCLUDED
void sdp_bqb_inact_timeout_ctrl(BOOLEAN enable)
{
s_sdp_bqb_inact_timeout_flag = enable;
}
#endif /* BT_SDP_BQB_INCLUDED */
/*******************************************************************************
**
** Function l2cap_bqb_ertm_mode_included_ctrl
**
** Description Control the L2CAP flow control and retransmissions mode for SDP BQB test
**
** Returns void
**
*******************************************************************************/
#if BT_SDP_BQB_INCLUDED
void l2cap_bqb_ertm_mode_included_ctrl(BOOLEAN enable)
{
l2cap_bqb_ertm_mode_included_flag = enable;
}
#endif /* BT_SDP_BQB_INCLUDED */
/*******************************************************************************
**
** Function sdp_init
**
** Description This function initializes the SDP unit.
**
** Returns void
**
*******************************************************************************/
void sdp_init (void)
{
#if SDP_DYNAMIC_MEMORY
sdp_cb_ptr = (tSDP_CB *)osi_malloc(sizeof(tSDP_CB));
#endif /* #if SDP_DYNAMIC_MEMORY */
/* Clears all structures and local SDP database (if Server is enabled) */
memset (&sdp_cb, 0, sizeof (tSDP_CB));
sdp_cb.server_db.p_record_list = list_new(osi_free_func);
/* 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;
#if BT_SDP_BQB_INCLUDED
if (l2cap_bqb_ertm_mode_included_flag) {
sdp_cb.l2cap_my_cfg.fcr_present = TRUE;
sdp_cb.l2cap_my_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE;
sdp_cb.l2cap_my_cfg.fcr.tx_win_sz = 8;
sdp_cb.l2cap_my_cfg.fcr.max_transmit = 0xff;
sdp_cb.l2cap_my_cfg.fcr.rtrans_tout = 2000;
sdp_cb.l2cap_my_cfg.fcr.mon_tout = 12000;
sdp_cb.l2cap_my_cfg.fcr.mps = 672;
}
#endif /* BT_SDP_BQB_INCLUDED */
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_ERROR ("Security Registration Server failed\n");
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_ERROR ("Security Registration for Client failed\n");
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_ERROR ("SDP Registration failed\n");
}
}
void sdp_deinit (void)
{
list_free(sdp_cb.server_db.p_record_list);
#if SDP_DYNAMIC_MEMORY
osi_free(sdp_cb_ptr);
sdp_cb_ptr = NULL;
#endif /* #if SDP_DYNAMIC_MEMORY */
}
#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)
{
UNUSED(psm);
#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_DEBUG("sdp_connect_ind: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u\n",
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_EVENT ("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x\n", 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_WARNING ("SDP - Rcvd conn cnf for unknown CID 0x%x\n", 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_DEBUG("sdp_connect_cfm: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u\n",
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_EVENT ("SDP - got conn cnf, sent cfg req, CID: 0x%x\n", p_ccb->connection_id);
} else {
SDP_TRACE_WARNING ("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x\n", result, p_ccb->connection_id);
/* Tell the user if he has a callback */
if (p_ccb->p_cb || p_ccb->p_cb2) {
UINT16 err = -1;
if ((result == HCI_ERR_HOST_REJECT_SECURITY)
|| (result == HCI_ERR_AUTH_FAILURE)
|| (result == HCI_ERR_PAIRING_NOT_ALLOWED)
|| (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED)
|| (result == HCI_ERR_KEY_MISSING)) {
err = SDP_SECURITY_ERR;
} else if (result == HCI_ERR_HOST_REJECT_DEVICE) {
err = SDP_CONN_REJECTED;
} else {
err = SDP_CONN_FAILED;
}
if (p_ccb->p_cb) {
(*p_ccb->p_cb)(err);
} else if (p_ccb->p_cb2) {
(*p_ccb->p_cb2)(err, p_ccb->user_data);
}
}
sdpu_release_ccb (p_ccb);
}
}
#endif /* SDP_CLIENT_ENABLED == TRUE */
/*******************************************************************************
**
** Function sdp_config_ind
**
** Description This function processes the L2CAP configuration indication
** event.
**
** Returns void
**
*******************************************************************************/
static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) {
SDP_TRACE_WARNING ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x\n", 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_DEBUG("sdp_config_ind(CONFIG) -> Please try again with SMALLER TX WINDOW\n");
}
/* 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_DEBUG("sdp_config_ind(CONFIG) -> Please try again with BASIC mode\n");
}
/* Remain in configure state and give the peer our desired configuration */
if (p_cfg->result != L2CAP_CFG_OK) {
SDP_TRACE_WARNING ("SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: 0x%x\n", 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_EVENT ("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x\n", 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_EVENT ("SDP - Rcvd cfg cfm, CID: 0x%x Result: %d\n", l2cap_cid, p_cfg->result);
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) {
SDP_TRACE_WARNING ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x\n", 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 */
{
#if BT_SDP_BQB_INCLUDED
/* Change the timeout from 30s to 90s for BQB test */
if (s_sdp_bqb_inact_timeout_flag) {
btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_BQB_INACT_TIMEOUT);
} else
#endif /* BT_SDP_BQB_INCLUDED */
{
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_WARNING ("SDP - Rcvd L2CAP disc, unknown CID: 0x%x\n", l2cap_cid);
return;
}
if (ack_needed) {
L2CA_DisconnectRsp (l2cap_cid);
}
SDP_TRACE_EVENT ("SDP - Rcvd L2CAP disc, CID: 0x%x\n", l2cap_cid);
#if SDP_CLIENT_ENABLED == TRUE
/* Tell the user if he has a callback */
if (p_ccb->p_cb) {
(*p_ccb->p_cb) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
SDP_SUCCESS : SDP_CONN_FAILED));
} else if (p_ccb->p_cb2) {
(*p_ccb->p_cb2) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ?
SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data);
}
#endif
sdpu_release_ccb (p_ccb);
}
/*******************************************************************************
**
** Function sdp_data_ind
**
** Description This function is called when data is received from L2CAP.
** if we are the originator of the connection, we are the SDP
** client, and the received message is queued up for the client.
**
** If we are the destination of the connection, we are the SDP
** server, so the message is passed to the server processing
** function.
**
** Returns void
**
*******************************************************************************/
static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
{
tCONN_CB *p_ccb;
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL) {
if (p_ccb->con_state == SDP_STATE_CONNECTED) {
#if BT_SDP_BQB_INCLUDED
/* Skip the following code in BQB test when the flag is true, since the PDU is reserved and
function sdp_server_handle_client_req will return error (sdpu_build_n_send_error) */
if (!s_sdp_bqb_disable_flag)
#endif /* BT_SDP_BQB_INCLUDED */
{
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_WARNING ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x\n",
p_ccb->con_state, l2cap_cid);
}
} else {
SDP_TRACE_WARNING ("SDP - Rcvd L2CAP data, unknown CID: 0x%x\n", l2cap_cid);
}
osi_free (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_WARNING ("SDP - no spare CCB for orig\n");
return (NULL);
}
SDP_TRACE_EVENT ("SDP - Originate started\n");
/* 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;
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_WARNING ("SDP - Originate failed\n");
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_EVENT ("SDP - looking for for more, CID: 0x%x\n",
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_EVENT ("SDP - disconnect CID: 0x%x\n", p_ccb->connection_id);
/* Check if we have a connection ID */
if (p_ccb->connection_id != 0) {
L2CA_DisconnectReq (p_ccb->connection_id);
p_ccb->disconnect_reason = reason;
}
/* If at setup state, we may not get callback ind from L2CAP */
/* Call user callback immediately */
if (p_ccb->con_state == SDP_STATE_CONN_SETUP) {
/* Tell the user if he has a callback */
if (p_ccb->p_cb) {
(*p_ccb->p_cb) (reason);
} else if (p_ccb->p_cb2) {
(*p_ccb->p_cb2) (reason, p_ccb->user_data);
}
sdpu_release_ccb (p_ccb);
}
}
/*******************************************************************************
**
** Function sdp_disconnect_cfm
**
** Description This function handles a disconnect confirm event from L2CAP.
**
** Returns void
**
*******************************************************************************/
static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
{
tCONN_CB *p_ccb;
UNUSED(result);
/* Find CCB based on CID */
if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) {
SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x\n", l2cap_cid);
return;
}
SDP_TRACE_EVENT ("SDP - Rcvd L2CAP disc cfm, CID: 0x%x, rsn %d\n", l2cap_cid, p_ccb->disconnect_reason);
/* Tell the user if he has a callback */
if (p_ccb->p_cb) {
(*p_ccb->p_cb) (p_ccb->disconnect_reason);
} else if (p_ccb->p_cb2) {
(*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data);
}
sdpu_release_ccb (p_ccb);
}
#endif /* SDP_CLIENT_ENABLED == TRUE */
/*******************************************************************************
**
** Function sdp_conn_timeout
**
** Description This function processes a timeout. Currently, we simply send
** a disconnect request to L2CAP.
**
** Returns void
**
*******************************************************************************/
void sdp_conn_timeout (tCONN_CB *p_ccb)
{
SDP_TRACE_EVENT ("SDP - CCB timeout in state: %d CID: 0x%x\n",
p_ccb->con_state, p_ccb->connection_id);
L2CA_DisconnectReq (p_ccb->connection_id);
#if SDP_CLIENT_ENABLED == TRUE
/* Tell the user if he has a callback */
if (p_ccb->p_cb) {
(*p_ccb->p_cb) (SDP_CONN_FAILED);
} else if (p_ccb->p_cb2) {
(*p_ccb->p_cb2) (SDP_CONN_FAILED, p_ccb->user_data);
}
#endif
sdpu_release_ccb (p_ccb);
}
#endif ///SDP_INCLUDED == TRUE
@@ -0,0 +1,838 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains functions that handle the SDP server functions.
* This is mainly dealing with client requests
*
******************************************************************************/
//#include <stdlib.h>
#include <string.h>
//#include <stdio.h>
#include "stack/bt_types.h"
#include "osi/allocator.h"
#include "stack/btu.h"
#include "common/bt_defs.h"
#include "stack/l2cdefs.h"
#include "stack/hcidefs.h"
#include "stack/hcimsgs.h"
#include "stack/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
#ifndef SDP_TEXT_BAD_MAX_RECORDS_LIST
#define SDP_TEXT_BAD_MAX_RECORDS_LIST 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_WARNING ("SDP - server got unknown PDU: 0x%x\n", 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] = {0};
tSDP_RECORD *p_rec = NULL;
BT_HDR *p_buf;
BOOLEAN is_cont = FALSE;
UNUSED(p_req_end);
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;
}
if ((!p_req) || (p_req > p_req_end)) {
sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_MAX_RECORDS_LIST);
return;
}
/* 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 || (p_req >= p_req_end)) {
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 || num_rsp_handles < 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;
p_ccb->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 *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
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_DEBUG("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) {
/* Free and reallocate buffer */
if (p_ccb->rsp_list) {
osi_free(p_ccb->rsp_list);
}
p_ccb->rsp_list = (UINT8 *)osi_malloc(max_list_len);
if (p_ccb->rsp_list == NULL) {
SDP_TRACE_ERROR("%s No scratch buf for attr rsp\n", __func__);
return;
}
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 {
if (p_ccb->rsp_list) {
osi_free (p_ccb->rsp_list);
}
p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
if (p_ccb->rsp_list == NULL) {
SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
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 >= SDP_MAX_ATTR_LEN) {
SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", 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 *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
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;
UNUSED(p_req_end);
/* 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) {
/* Free and reallocate buffer */
if (p_ccb->rsp_list) {
osi_free (p_ccb->rsp_list);
}
p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
if (p_ccb->rsp_list == NULL) {
SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
return;
}
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 */
/* Free and reallocate if the earlier allocated buffer is small */
if (p_ccb->rsp_list) {
osi_free (p_ccb->rsp_list);
}
p_ccb->rsp_list = (UINT8 *)osi_malloc (max_list_len);
if (p_ccb->rsp_list == NULL) {
SDP_TRACE_ERROR ("SDP - no scratch buf for search rsp\n");
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 >= SDP_MAX_ATTR_LEN) {
SDP_TRACE_ERROR("SDP attr too big: max_list_len=%d,attr_len=%d\n", 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;
// The current SDP server design has a critical flaw where it can run into an infinite
// request/response loop with the client. Here's the scenario:
// - client makes SDP request
// - server returns the first fragment of the response with a continuation token
// - an SDP record is deleted from the server
// - client issues another request with previous continuation token
// - server has nothing to send back because the record is unavailable but in the
// first fragment, it had specified more response bytes than are now available
// - server sends back no additional response bytes and returns the same continuation token
// - client issues another request with the continuation token, and the process repeats
//
// We work around this design flaw here by checking if we will make forward progress
// (i.e. we will send > 0 response bytes) on a continued request. If not, we must have
// run into the above situation and we tell the peer an error occurred.
//
// TODO(sharvil): rewrite SDP server.
if (is_cont && len_to_send == 0) {
sdpu_build_n_send_error(p_ccb, trans_num, SDP_INVALID_CONT_STATE, NULL);
return;
}
/* 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 *)osi_malloc(SDP_DATA_BUF_SIZE)) == NULL) {
SDP_TRACE_ERROR ("SDP - no buf for search rsp\n");
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 */
File diff suppressed because it is too large Load Diff