Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
This commit is contained in:
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user