summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Schulz <k.schulz@samsung.com>2015-03-25 10:39:40 +0100
committerAndre Eisenbach <eisenbach@google.com>2015-04-10 16:49:17 -0700
commit8372aa5fa535ee4f09c09981b6125b54ace31fe2 (patch)
tree7dcba062c3e78e4475cd925706fc08201444118d
parentfd422a772fb3d28d462a2b8020ff4a2bdc5e954a (diff)
downloadandroid_system_bt-8372aa5fa535ee4f09c09981b6125b54ace31fe2.tar.gz
android_system_bt-8372aa5fa535ee4f09c09981b6125b54ace31fe2.tar.bz2
android_system_bt-8372aa5fa535ee4f09c09981b6125b54ace31fe2.zip
L2CAP and SDP Search API for BT profiles (2/2)
Added support for exposing L2CAP to Java such that OBEX over L2CAP is made possible. Added support to create SDP records as a seperate step.(as opposed to creating a SDP record when a BluetoothSocket is created). This allows both a RFCOMM channel and a L2CAP PSM to be included in a SDP record. (Additionally the content of the SDP record is set by the profile in Java, in stead of beeing hardcoded in the socket layer.) This completes the L2CAP channel exposure to Java. Change-Id: Iaf68a07d910145cdd33e940d73cd680f79164100
-rw-r--r--bta/Android.mk10
-rw-r--r--bta/dm/bta_dm_pm.c5
-rw-r--r--bta/include/bta_api.h24
-rw-r--r--bta/include/bta_jv_api.h410
-rw-r--r--bta/include/bta_jv_co.h4
-rw-r--r--bta/include/bta_sdp_api.h145
-rw-r--r--bta/jv/bta_jv_act.c1255
-rw-r--r--bta/jv/bta_jv_api.c604
-rw-r--r--bta/jv/bta_jv_int.h161
-rw-r--r--bta/jv/bta_jv_main.c13
-rw-r--r--bta/sdp/bta_sdp.c75
-rw-r--r--bta/sdp/bta_sdp_act.c570
-rw-r--r--bta/sdp/bta_sdp_api.c173
-rw-r--r--bta/sdp/bta_sdp_cfg.c40
-rw-r--r--bta/sdp/bta_sdp_int.h115
-rw-r--r--bta/sys/bta_sys.h9
-rw-r--r--btif/include/btif_sdp.h34
-rw-r--r--btif/include/btif_sock_l2cap.h23
-rw-r--r--btif/include/btif_sock_sdp.h4
-rw-r--r--btif/src/bluetooth.c7
-rw-r--r--btif/src/btif_core.c1
-rw-r--r--btif/src/btif_dm.c8
-rw-r--r--btif/src/btif_sdp.c197
-rw-r--r--btif/src/btif_sdp_server.c721
-rw-r--r--btif/src/btif_sock.c29
-rw-r--r--btif/src/btif_sock_l2cap.c1070
-rw-r--r--btif/src/btif_sock_rfc.c135
-rw-r--r--btif/src/btif_sock_sdp.c98
-rw-r--r--btif/src/btif_sock_thread.c21
-rw-r--r--btif/src/btif_sock_util.c2
-rw-r--r--conf/bt_stack.conf1
-rw-r--r--include/bt_target.h126
-rw-r--r--main/Android.mk4
-rw-r--r--stack/Android.mk2
-rw-r--r--stack/gap/gap_conn.c1284
-rw-r--r--stack/gap/gap_int.h79
-rw-r--r--stack/gap/gap_utils.c436
-rw-r--r--stack/gatt/gatt_main.c12
-rw-r--r--stack/include/gap_api.h234
-rw-r--r--stack/include/l2c_api.h6
-rw-r--r--stack/include/l2cdefs.h5
-rw-r--r--stack/include/sdpdefs.h6
-rw-r--r--stack/l2cap/l2c_api.c4
-rw-r--r--stack/l2cap/l2c_ble.c18
-rw-r--r--stack/l2cap/l2c_csm.c13
-rw-r--r--stack/l2cap/l2c_fcr.c3
-rw-r--r--stack/l2cap/l2c_link.c8
-rw-r--r--stack/l2cap/l2c_main.c3
-rw-r--r--stack/l2cap/l2c_utils.c38
-rw-r--r--stack/smp/smp_l2c.c9
50 files changed, 8001 insertions, 253 deletions
diff --git a/bta/Android.mk b/bta/Android.mk
index e5e853f27..cb11643d1 100644
--- a/bta/Android.mk
+++ b/bta/Android.mk
@@ -70,17 +70,17 @@ LOCAL_SRC_FILES:= \
./hl/bta_hl_utils.c \
./hl/bta_hl_sdp.c \
./hl/bta_hl_ci.c \
- ./mce/bta_mce_api.c \
- ./mce/bta_mce_main.c \
- ./mce/bta_mce_act.c \
- ./mce/bta_mce_cfg.c \
+ ./sdp/bta_sdp_api.c \
+ ./sdp/bta_sdp_act.c \
+ ./sdp/bta_sdp.c \
+ ./sdp/bta_sdp_cfg.c \
./sys/bta_sys_main.c \
./sys/bta_sys_conn.c \
./sys/utl.c \
./jv/bta_jv_act.c \
./jv/bta_jv_cfg.c \
./jv/bta_jv_main.c \
- ./jv/bta_jv_api.c \
+ ./jv/bta_jv_api.c
LOCAL_MODULE := libbt-brcm_bta
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
diff --git a/bta/dm/bta_dm_pm.c b/bta/dm/bta_dm_pm.c
index c3c05de53..7cee9c35a 100644
--- a/bta/dm/bta_dm_pm.c
+++ b/bta/dm/bta_dm_pm.c
@@ -124,6 +124,7 @@ void bta_dm_disable_pm(void)
static void bta_dm_pm_stop_timer(BD_ADDR peer_addr)
{
UINT8 i;
+ APPL_TRACE_DEBUG("%s: ", __func__);
for(i=0; i<BTA_DM_NUM_PM_TIMER; i++)
{
@@ -448,8 +449,8 @@ static void bta_dm_pm_set_mode(BD_ADDR peer_addr, BOOLEAN timed_out )
/* dont initiate SNIFF, if link_policy has it disabled */
if (p_peer_device->link_policy & HCI_ENABLE_SNIFF_MODE)
{
- p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF;
- bta_dm_pm_sniff(p_peer_device, (UINT8)(pm_action & 0x0F) );
+ p_peer_device->pm_mode_attempted = BTA_DM_PM_SNIFF;
+ bta_dm_pm_sniff(p_peer_device, (UINT8)(pm_action & 0x0F) );
}
else
{
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index c7aab8322..efa8d32d1 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -85,19 +85,19 @@ typedef UINT8 tBTA_STATUS;
#define BTA_MN_SERVICE_ID 26 /* Message Notification Service */
#define BTA_HDP_SERVICE_ID 27 /* Health Device Profile */
#define BTA_PCE_SERVICE_ID 28 /* PhoneBook Access Client*/
-
+#define BTA_SDP_SERVICE_ID 29 /* SDP Search*/
#if BLE_INCLUDED == TRUE && BTA_GATT_INCLUDED == TRUE
/* BLE profile service ID */
-#define BTA_BLE_SERVICE_ID 29 /* GATT profile */
+#define BTA_BLE_SERVICE_ID 30 /* GATT profile */
// btla-specific ++
-#define BTA_USER_SERVICE_ID 30 /* User requested UUID */
+#define BTA_USER_SERVICE_ID 31 /* User requested UUID */
-#define BTA_MAX_SERVICE_ID 31
+#define BTA_MAX_SERVICE_ID 32
// btla-specific --
#else
-#define BTA_USER_SERVICE_ID 29 /* User requested UUID */
-#define BTA_MAX_SERVICE_ID 30
+#define BTA_USER_SERVICE_ID 30 /* User requested UUID */
+#define BTA_MAX_SERVICE_ID 31
#endif
/* service IDs (BTM_SEC_SERVICE_FIRST_EMPTY + 1) to (BTM_SEC_MAX_SERVICES - 1)
* are used by BTA JV */
@@ -214,7 +214,7 @@ typedef tBT_TRANSPORT tBTA_TRANSPORT;
#define BTA_DM_CONN_PAIRED 1
/* Inquiry Modes */
-#define BTA_DM_INQUIRY_NONE BTM_INQUIRY_NONE /*No BR inquiry. */
+#define BTA_DM_INQUIRY_NONE BTM_INQUIRY_NONE /*No BR inquiry. */
#define BTA_DM_GENERAL_INQUIRY BTM_GENERAL_INQUIRY /* Perform general inquiry. */
#define BTA_DM_LIMITED_INQUIRY BTM_LIMITED_INQUIRY /* Perform limited inquiry. */
@@ -497,10 +497,10 @@ typedef tBTM_BLE_TRACK_ADV_ACTION tBTA_BLE_TRACK_ADV_ACTION;
#define BTA_BLE_RSSI_ALERT_LO 2
typedef UINT8 tBTA_DM_BLE_RSSI_ALERT_TYPE;
-#define BTA_BLE_RSSI_ALERT_NONE BTM_BLE_RSSI_ALERT_NONE /* (0) */
-#define BTA_BLE_RSSI_ALERT_HI_BIT BTM_BLE_RSSI_ALERT_HI_BIT /* (1) */
-#define BTA_BLE_RSSI_ALERT_RANGE_BIT BTM_BLE_RSSI_ALERT_RANGE_BIT /* (1 << 1) */
-#define BTA_BLE_RSSI_ALERT_LO_BIT BTM_BLE_RSSI_ALERT_LO_BIT /* (1 << 2) */
+#define BTA_BLE_RSSI_ALERT_NONE BTM_BLE_RSSI_ALERT_NONE /* (0) */
+#define BTA_BLE_RSSI_ALERT_HI_BIT BTM_BLE_RSSI_ALERT_HI_BIT /* (1) */
+#define BTA_BLE_RSSI_ALERT_RANGE_BIT BTM_BLE_RSSI_ALERT_RANGE_BIT /* (1 << 1) */
+#define BTA_BLE_RSSI_ALERT_LO_BIT BTM_BLE_RSSI_ALERT_LO_BIT /* (1 << 2) */
typedef UINT8 tBTA_DM_BLE_RSSI_ALERT_MASK;
@@ -510,7 +510,7 @@ typedef void (tBTA_DM_BLE_RSSI_CBACK) (BD_ADDR bd_addr, tBTA_DM_BLE_RSSI_ALERT_T
#define BTA_DM_BLE_MAX_UUID_FILTER BTM_BLE_MAX_UUID_FILTER /* 8 */
#define BTA_DM_BLE_MAX_ADDR_FILTER BTM_BLE_MAX_ADDR_FILTER /* 8 */
#define BTA_DM_BLE_PF_STR_COND_MAX BTM_BLE_PF_STR_COND_MAX /* 4 apply to manu data , or local name */
-#define BTA_DM_BLE_PF_STR_LEN_MAX BTM_BLE_PF_STR_LEN_MAX /* match for first 20 bytes */
+#define BTA_DM_BLE_PF_STR_LEN_MAX BTM_BLE_PF_STR_LEN_MAX /* match for first 20 bytes */
#define BTA_DM_BLE_PF_LOGIC_OR 0
#define BTA_DM_BLE_PF_LOGIC_AND 1
diff --git a/bta/include/bta_jv_api.h b/bta/include/bta_jv_api.h
index a61588a16..9996da98a 100644
--- a/bta/include/bta_jv_api.h
+++ b/bta/include/bta_jv_api.h
@@ -28,6 +28,8 @@
#include "bt_types.h"
#include "bta_api.h"
#include "btm_api.h"
+#include "l2c_api.h"
+
/*****************************************************************************
** Constants and data types
*****************************************************************************/
@@ -44,6 +46,7 @@ typedef UINT8 tBTA_JV_STATUS;
#define BTA_JV_MAX_UUIDS SDP_MAX_UUID_FILTERS
#define BTA_JV_MAX_ATTRS SDP_MAX_ATTR_FILTERS
#define BTA_JV_MAX_SDP_REC SDP_MAX_RECORDS
+#define BTA_JV_MAX_L2C_CONN GAP_MAX_CONNECTIONS /* GAP handle is used as index, hence do not change this value */
#define BTA_JV_MAX_SCN PORT_MAX_RFC_PORTS /* same as BTM_MAX_SCN (in btm_int.h) */
#define BTA_JV_MAX_RFC_CONN MAX_RFC_PORTS
@@ -51,7 +54,6 @@ typedef UINT8 tBTA_JV_STATUS;
#define BTA_JV_DEF_RFC_MTU (3*330)
#endif
-/* */
#ifndef BTA_JV_MAX_RFC_SR_SESSION
#define BTA_JV_MAX_RFC_SR_SESSION MAX_BD_CONNECTIONS
#endif
@@ -118,23 +120,41 @@ enum
};
typedef UINT8 tBTA_JV_CONN_STATE;
+/* JV Connection types */
+#define BTA_JV_CONN_TYPE_RFCOMM 0
+#define BTA_JV_CONN_TYPE_L2CAP 1
+#define BTA_JV_CONN_TYPE_L2CAP_LE 2
+
/* Java I/F callback events */
/* events received by tBTA_JV_DM_CBACK */
#define BTA_JV_ENABLE_EVT 0 /* JV enabled */
+#define BTA_JV_GET_SCN_EVT 6 /* Reserved an SCN */
+#define BTA_JV_GET_PSM_EVT 7 /* Reserved a PSM */
#define BTA_JV_DISCOVERY_COMP_EVT 8 /* SDP discovery complete */
#define BTA_JV_CREATE_RECORD_EVT 11 /* the result for BTA_JvCreateRecord */
+/* events received by tBTA_JV_L2CAP_CBACK */
+#define BTA_JV_L2CAP_OPEN_EVT 16 /* open status of L2CAP connection */
+#define BTA_JV_L2CAP_CLOSE_EVT 17 /* L2CAP connection closed */
+#define BTA_JV_L2CAP_START_EVT 18 /* L2CAP server started */
+#define BTA_JV_L2CAP_CL_INIT_EVT 19 /* L2CAP client initiated a connection */
+#define BTA_JV_L2CAP_DATA_IND_EVT 20 /* L2CAP connection received data */
+#define BTA_JV_L2CAP_CONG_EVT 21 /* L2CAP connection congestion status changed */
+#define BTA_JV_L2CAP_READ_EVT 22 /* the result for BTA_JvL2capRead */
+#define BTA_JV_L2CAP_RECEIVE_EVT 23 /* the result for BTA_JvL2capReceive*/
+#define BTA_JV_L2CAP_WRITE_EVT 24 /* the result for BTA_JvL2capWrite*/
+#define BTA_JV_L2CAP_WRITE_FIXED_EVT 25 /* the result for BTA_JvL2capWriteFixed */
/* events received by tBTA_JV_RFCOMM_CBACK */
-#define BTA_JV_RFCOMM_OPEN_EVT 25 /* open status of RFCOMM Client connection */
-#define BTA_JV_RFCOMM_CLOSE_EVT 26 /* RFCOMM connection closed */
-#define BTA_JV_RFCOMM_START_EVT 27 /* RFCOMM server started */
-#define BTA_JV_RFCOMM_CL_INIT_EVT 28 /* RFCOMM client initiated a connection */
-#define BTA_JV_RFCOMM_DATA_IND_EVT 29 /* RFCOMM connection received data */
-#define BTA_JV_RFCOMM_CONG_EVT 30 /* RFCOMM connection congestion status changed */
-#define BTA_JV_RFCOMM_READ_EVT 31 /* the result for BTA_JvRfcommRead */
-#define BTA_JV_RFCOMM_WRITE_EVT 32 /* the result for BTA_JvRfcommWrite*/
-#define BTA_JV_RFCOMM_SRV_OPEN_EVT 33 /* open status of Server RFCOMM connection */
-#define BTA_JV_MAX_EVT 34 /* max number of JV events */
+#define BTA_JV_RFCOMM_OPEN_EVT 26 /* open status of RFCOMM Client connection */
+#define BTA_JV_RFCOMM_CLOSE_EVT 27 /* RFCOMM connection closed */
+#define BTA_JV_RFCOMM_START_EVT 28 /* RFCOMM server started */
+#define BTA_JV_RFCOMM_CL_INIT_EVT 29 /* RFCOMM client initiated a connection */
+#define BTA_JV_RFCOMM_DATA_IND_EVT 30 /* RFCOMM connection received data */
+#define BTA_JV_RFCOMM_CONG_EVT 31 /* RFCOMM connection congestion status changed */
+#define BTA_JV_RFCOMM_READ_EVT 32 /* the result for BTA_JvRfcommRead */
+#define BTA_JV_RFCOMM_WRITE_EVT 33 /* the result for BTA_JvRfcommWrite*/
+#define BTA_JV_RFCOMM_SRV_OPEN_EVT 34 /* open status of Server RFCOMM connection */
+#define BTA_JV_MAX_EVT 35 /* max number of JV events */
typedef UINT16 tBTA_JV_EVT;
@@ -158,6 +178,104 @@ typedef struct
tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
} tBTA_JV_CREATE_RECORD;
+/* data associated with BTA_JV_L2CAP_OPEN_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ BD_ADDR rem_bda; /* The peer address */
+ INT32 tx_mtu; /* The transmit MTU */
+} tBTA_JV_L2CAP_OPEN;
+
+/* data associated with BTA_JV_L2CAP_OPEN_EVT for LE sockets */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ BD_ADDR rem_bda; /* The peer address */
+ INT32 tx_mtu; /* The transmit MTU */
+ void **p_p_cback; /* set them for new socket */
+ void **p_user_data;/* set them for new socket */
+
+} tBTA_JV_L2CAP_LE_OPEN;
+
+
+/* data associated with BTA_JV_L2CAP_CLOSE_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ BOOLEAN async; /* FALSE, if local initiates disconnect */
+} tBTA_JV_L2CAP_CLOSE;
+
+/* data associated with BTA_JV_L2CAP_START_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ UINT8 sec_id; /* security ID used by this server */
+} tBTA_JV_L2CAP_START;
+
+/* data associated with BTA_JV_L2CAP_CL_INIT_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ UINT8 sec_id; /* security ID used by this client */
+} tBTA_JV_L2CAP_CL_INIT;
+
+/* data associated with BTA_JV_L2CAP_CONG_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ BOOLEAN cong; /* TRUE, congested. FALSE, uncongested */
+} tBTA_JV_L2CAP_CONG;
+
+/* data associated with BTA_JV_L2CAP_READ_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ UINT32 req_id; /* The req_id in the associated BTA_JvL2capRead() */
+ UINT8 *p_data; /* This points the same location as the p_data
+ * parameter in BTA_JvL2capRead () */
+ UINT16 len; /* The length of the data read. */
+} tBTA_JV_L2CAP_READ;
+
+/* data associated with BTA_JV_L2CAP_RECEIVE_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ UINT32 req_id; /* The req_id in the associated BTA_JvL2capReceive() */
+ UINT8 *p_data; /* This points the same location as the p_data
+ * parameter in BTA_JvL2capReceive () */
+ UINT16 len; /* The length of the data read. */
+} tBTA_JV_L2CAP_RECEIVE;
+
+/* data associated with BTA_JV_L2CAP_WRITE_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT32 handle; /* The connection handle */
+ UINT32 req_id; /* The req_id in the associated BTA_JvL2capWrite() */
+ UINT16 len; /* The length of the data written. */
+ BOOLEAN cong; /* congestion status */
+} tBTA_JV_L2CAP_WRITE;
+
+
+/* data associated with BTA_JV_L2CAP_WRITE_FIXED_EVT */
+typedef struct
+{
+ tBTA_JV_STATUS status; /* Whether the operation succeeded or failed. */
+ UINT16 channel; /* The connection channel */
+ BD_ADDR addr; /* The peer address */
+ UINT32 req_id; /* The req_id in the associated BTA_JvL2capWrite() */
+ UINT16 len; /* The length of the data written. */
+ BOOLEAN cong; /* congestion status */
+} tBTA_JV_L2CAP_WRITE_FIXED;
+
/* data associated with BTA_JV_RFCOMM_OPEN_EVT */
typedef struct
{
@@ -207,6 +325,14 @@ typedef struct
UINT32 handle; /* The connection handle */
} tBTA_JV_DATA_IND;
+/*data associated with BTA_JV_L2CAP_DATA_IND_EVT if used for LE */
+typedef struct
+{
+ UINT32 handle; /* The connection handle */
+ BT_HDR *p_buf; /* The incoming data */
+} tBTA_JV_LE_DATA_IND;
+
+
/* data associated with BTA_JV_RFCOMM_CONG_EVT */
typedef struct
{
@@ -258,7 +384,16 @@ typedef union
tBTA_JV_STATUS status; /* BTA_JV_ENABLE_EVT */
tBTA_JV_DISCOVERY_COMP disc_comp; /* BTA_JV_DISCOVERY_COMP_EVT */
tBTA_JV_SET_DISCOVER set_discover; /* BTA_JV_SET_DISCOVER_EVT */
+ UINT8 scn; /* BTA_JV_GET_SCN_EVT */
+ UINT16 psm; /* BTA_JV_GET_PSM_EVT */
tBTA_JV_CREATE_RECORD create_rec; /* BTA_JV_CREATE_RECORD_EVT */
+ tBTA_JV_L2CAP_OPEN l2c_open; /* BTA_JV_L2CAP_OPEN_EVT */
+ tBTA_JV_L2CAP_CLOSE l2c_close; /* BTA_JV_L2CAP_CLOSE_EVT */
+ tBTA_JV_L2CAP_START l2c_start; /* BTA_JV_L2CAP_START_EVT */
+ tBTA_JV_L2CAP_CL_INIT l2c_cl_init; /* BTA_JV_L2CAP_CL_INIT_EVT */
+ tBTA_JV_L2CAP_CONG l2c_cong; /* BTA_JV_L2CAP_CONG_EVT */
+ tBTA_JV_L2CAP_READ l2c_read; /* BTA_JV_L2CAP_READ_EVT */
+ tBTA_JV_L2CAP_WRITE l2c_write; /* BTA_JV_L2CAP_WRITE_EVT */
tBTA_JV_RFCOMM_OPEN rfc_open; /* BTA_JV_RFCOMM_OPEN_EVT */
tBTA_JV_RFCOMM_SRV_OPEN rfc_srv_open; /* BTA_JV_RFCOMM_SRV_OPEN_EVT */
tBTA_JV_RFCOMM_CLOSE rfc_close; /* BTA_JV_RFCOMM_CLOSE_EVT */
@@ -267,8 +402,11 @@ typedef union
tBTA_JV_RFCOMM_CONG rfc_cong; /* BTA_JV_RFCOMM_CONG_EVT */
tBTA_JV_RFCOMM_READ rfc_read; /* BTA_JV_RFCOMM_READ_EVT */
tBTA_JV_RFCOMM_WRITE rfc_write; /* BTA_JV_RFCOMM_WRITE_EVT */
- tBTA_JV_DATA_IND data_ind; /* BTA_JV_L2CAP_DATA_IND_EVT
+ tBTA_JV_DATA_IND data_ind; /* BTA_JV_L2CAP_DATA_IND_EVT
BTA_JV_RFCOMM_DATA_IND_EVT */
+ tBTA_JV_LE_DATA_IND le_data_ind; /* BTA_JV_L2CAP_LE_DATA_IND_EVT */
+ tBTA_JV_L2CAP_LE_OPEN l2c_le_open; /* BTA_JV_L2CAP_OPEN_EVT */
+ tBTA_JV_L2CAP_WRITE_FIXED l2c_write_fixed; /* BTA_JV_L2CAP_WRITE_FIXED_EVT */
} tBTA_JV;
/* JAVA DM Interface callback */
@@ -277,6 +415,9 @@ typedef void (tBTA_JV_DM_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void * user_
/* JAVA RFCOMM interface callback */
typedef void* (tBTA_JV_RFCOMM_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data);
+/* JAVA L2CAP interface callback */
+typedef void (tBTA_JV_L2CAP_CBACK)(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_Data);
+
/* JV configuration structure */
typedef struct
{
@@ -338,6 +479,43 @@ extern BOOLEAN BTA_JvIsEncrypted(BD_ADDR bd_addr);
/*******************************************************************************
**
+** Function BTA_JvGetChannelId
+**
+** Description This function reserves a SCN/PSM for applications running
+** over RFCOMM or L2CAP. It is primarily called by
+** server profiles/applications to register their SCN/PSM into the
+** SDP database. The SCN is reported by the tBTA_JV_DM_CBACK
+** callback with a BTA_JV_GET_SCN_EVT.
+** If the SCN/PSM reported is 0, that means all SCN resources are
+** exhausted.
+** The channel parameter can be used to request a specific
+** channel. If the request on the specific channel fails, the
+** SCN/PSM returned in the EVT will be 0 - no attempt to request
+** a new channel will be made. set channel to <= 0 to automatically
+** assign an channel ID.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvGetChannelId(int conn_type, void* user_data,
+ INT32 channel);
+
+/*******************************************************************************
+**
+** Function BTA_JvFreeChannel
+**
+** Description This function frees a SCN/PSM that was used
+** by an application running over RFCOMM or L2CAP.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvFreeChannel(UINT16 channel, int conn_type);
+
+/*******************************************************************************
+**
** Function BTA_JvStartDiscovery
**
** Description This function performs service discovery for the services
@@ -379,6 +557,214 @@ extern tBTA_JV_STATUS BTA_JvDeleteRecord(UINT32 handle);
/*******************************************************************************
**
+** Function BTA_JvL2capConnectLE
+**
+** Description Initiate a connection as an LE L2CAP client to the given BD
+** Address.
+** When the connection is initiated or failed to initiate,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT
+** When the connection is established or failed,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capConnectLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_chan,
+ UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capConnect
+**
+** Description Initiate a connection as a L2CAP client to the given BD
+** Address.
+** When the connection is initiated or failed to initiate,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT
+** When the connection is established or failed,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_psm,
+ UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capClose
+**
+** Description This function closes an L2CAP client connection
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capCloseLE
+**
+** Description This function closes an L2CAP client connection for Fixed Channels
+** Function is idempotent and no callbacks are called!
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capCloseLE(UINT32 handle);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStartServer
+**
+** Description This function starts an L2CAP server and listens for an L2CAP
+** connection from a remote Bluetooth device. When the server
+** is started successfully, tBTA_JV_L2CAP_CBACK is called with
+** BTA_JV_L2CAP_START_EVT. When the connection is established,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capStartServer(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info,
+ UINT16 local_psm, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ tBTA_JV_L2CAP_CBACK *p_cback, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStartServerLE
+**
+** Description This function starts an LE L2CAP server and listens for an L2CAP
+** connection from a remote Bluetooth device on a fixed channel
+** over an LE link. When the server
+** is started successfully, tBTA_JV_L2CAP_CBACK is called with
+** BTA_JV_L2CAP_START_EVT. When the connection is established,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capStartServerLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info,
+ UINT16 local_chan, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ tBTA_JV_L2CAP_CBACK *p_cback, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStopServerLE
+**
+** Description This function stops the LE L2CAP server. If the server has an
+** active connection, it would be closed.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capStopServerLE(UINT16 local_chan, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStopServerLE
+**
+** Description This function stops the LE L2CAP server. If the server has an
+** active connection, it would be closed.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capStopServer(UINT16 local_psm, void *user_data);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capRead
+**
+** Description This function reads data from an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_READ_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capRead(UINT32 handle, UINT32 req_id,
+ UINT8 *p_data, UINT16 len);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capReceive
+**
+** Description This function reads data from an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_RECEIVE_EVT.
+** If there are more data queued in L2CAP than len, the extra data will be discarded.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capReceive(UINT32 handle, UINT32 req_id,
+ UINT8 *p_data, UINT16 len);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capReady
+**
+** Description This function determined if there is data to read from
+** an L2CAP connection
+**
+** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size.
+** BTA_JV_FAILURE, if error.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capReady(UINT32 handle, UINT32 *p_data_size);
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capWrite
+**
+** Description This function writes data to an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_WRITE_EVT. Works for
+** PSM-based connections
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capWrite(UINT32 handle, UINT32 req_id,
+ UINT8 *p_data, UINT16 len, void *user_data);
+
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capWriteFixed
+**
+** Description This function writes data to an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_WRITE_FIXED_EVT. Works for
+** fixed-channel connections
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT32 req_id,
+ tBTA_JV_L2CAP_CBACK *p_cback,
+ UINT8 *p_data, UINT16 len, void *user_data);
+
+/*******************************************************************************
+**
** Function BTA_JvRfcommConnect
**
** Description This function makes an RFCOMM conection to a remote BD
diff --git a/bta/include/bta_jv_co.h b/bta/include/bta_jv_co.h
index 24315c207..198424890 100644
--- a/bta/include/bta_jv_co.h
+++ b/bta/include/bta_jv_co.h
@@ -46,4 +46,8 @@ extern int bta_co_rfc_data_incoming(void *user_data, BT_HDR *p_buf);
extern int bta_co_rfc_data_outgoing_size(void *user_data, int *size);
extern int bta_co_rfc_data_outgoing(void *user_data, UINT8* buf, UINT16 size);
+extern int bta_co_l2cap_data_incoming(void *user_data, BT_HDR *p_buf);
+extern int bta_co_l2cap_data_outgoing_size(void *user_data, int *size);
+extern int bta_co_l2cap_data_outgoing(void *user_data, UINT8* buf, UINT16 size);
+
#endif /* BTA_DG_CO_H */
diff --git a/bta/include/bta_sdp_api.h b/bta/include/bta_sdp_api.h
new file mode 100644
index 000000000..6fa13137c
--- /dev/null
+++ b/bta/include/bta_sdp_api.h
@@ -0,0 +1,145 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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 is the public interface file for the BTA SDP I/F
+ *
+ ******************************************************************************/
+#ifndef BTA_SDP_API_H
+#define BTA_SDP_API_H
+
+#include <hardware/bt_sdp.h>
+#include "bt_target.h"
+#include "bt_types.h"
+#include "bta_api.h"
+#include "btm_api.h"
+
+/* status values */
+#define BTA_SDP_SUCCESS 0 /* Successful operation. */
+#define BTA_SDP_FAILURE 1 /* Generic failure. */
+#define BTA_SDP_BUSY 2 /* Temporarily can not handle this request. */
+
+typedef UINT8 tBTA_SDP_STATUS;
+
+/* SDP I/F callback events */
+/* events received by tBTA_SDP_DM_CBACK */
+#define BTA_SDP_ENABLE_EVT 0 /* SDP service i/f enabled*/
+#define BTA_SDP_SEARCH_EVT 1 /* SDP Service started */
+#define BTA_SDP_SEARCH_COMP_EVT 2 /* SDP search complete */
+#define BTA_SDP_CREATE_RECORD_USER_EVT 3 /* SDP search complete */
+#define BTA_SDP_REMOVE_RECORD_USER_EVT 4 /* SDP search complete */
+#define BTA_SDP_MAX_EVT 5 /* max number of SDP events */
+
+#define BTA_SDP_MAX_RECORDS 15
+
+typedef UINT16 tBTA_SDP_EVT;
+
+/* data associated with BTA_SDP_DISCOVERY_COMP_EVT */
+typedef struct
+{
+ tBTA_SDP_STATUS status;
+ BD_ADDR remote_addr;
+ tBT_UUID uuid;
+ int record_count;
+ bluetooth_sdp_record records[BTA_SDP_MAX_RECORDS];
+} tBTA_SDP_SEARCH_COMP;
+
+typedef union
+{
+ tBTA_SDP_STATUS status; /* BTA_SDP_SEARCH_EVT */
+ tBTA_SDP_SEARCH_COMP sdp_search_comp; /* BTA_SDP_SEARCH_COMP_EVT */
+} tBTA_SDP;
+
+/* SDP DM Interface callback */
+typedef void (tBTA_SDP_DM_CBACK)(tBTA_SDP_EVT event, tBTA_SDP *p_data, void * user_data);
+
+/* MCE configuration structure */
+typedef struct
+{
+ UINT16 sdp_db_size; /* The size of p_sdp_db */
+ tSDP_DISCOVERY_DB *p_sdp_db; /* The data buffer to keep SDP database */
+} tBTA_SDP_CFG;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/*******************************************************************************
+**
+** Function BTA_SdpEnable
+**
+** Description Enable the SDP I/F service. When the enable
+** operation is complete the callback function will be
+** called with a BTA_SDP_ENABLE_EVT. This function must
+** be called before other functions in the MCE API are
+** called.
+**
+** Returns BTA_SDP_SUCCESS if successful.
+** BTA_SDP_FAIL if internal failure.
+**
+*******************************************************************************/
+extern tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback);
+
+/*******************************************************************************
+**
+** Function BTA_SdpSearch
+**
+** Description Start a search for sdp records for a specific BD_ADDR with a
+** specific profile uuid.
+** When the search operation is completed, the callback function
+** will be called with a BTA_SDP_SEARCH_EVT.
+** Returns BTA_SDP_SUCCESS if successful.
+** BTA_SDP_FAIL if internal failure.
+**
+*******************************************************************************/
+extern tBTA_SDP_STATUS BTA_SdpSearch(BD_ADDR bd_addr,tSDP_UUID *uuid);
+
+/*******************************************************************************
+**
+** Function BTA_SdpCreateRecordByUser
+**
+** Description This function is used to request a callback to create a SDP
+** record. The registered callback will be called with event
+** BTA_SDP_CREATE_RECORD_USER_EVT.
+**
+** Returns BTA_SDP_SUCCESS, if the request is being processed.
+** BTA_SDP_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_SDP_STATUS BTA_SdpCreateRecordByUser(void* user_data);
+
+/*******************************************************************************
+**
+** Function BTA_SdpRemoveRecordByUser
+**
+** Description This function is used to request a callback to remove a SDP
+** record. The registered callback will be called with event
+** BTA_SDP_REMOVE_RECORD_USER_EVT.
+**
+** Returns BTA_SDP_SUCCESS, if the request is being processed.
+** BTA_SDP_FAILURE, otherwise.
+**
+*******************************************************************************/
+extern tBTA_SDP_STATUS BTA_SdpRemoveRecordByUser(void* user_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BTA_SDP_API_H */
diff --git a/bta/jv/bta_jv_act.c b/bta/jv/bta_jv_act.c
index 224450ad6..46b8f7b38 100644
--- a/bta/jv/bta_jv_act.c
+++ b/bta/jv/bta_jv_act.c
@@ -23,6 +23,7 @@
******************************************************************************/
#include <hardware/bluetooth.h>
#include <arpa/inet.h>
+#include <pthread.h>
#include "bt_types.h"
#include "gki.h"
@@ -42,6 +43,57 @@
#include "avct_api.h"
#include "avdt_api.h"
#include "gap_api.h"
+#include "l2c_api.h"
+
+
+/* one of these exists for each client */
+struct fc_client {
+ struct fc_client *next_all_list;
+ struct fc_client *next_chan_list;
+ BD_ADDR remote_addr;
+ uint32_t id;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ void *user_data;
+ uint16_t handle;
+ uint16_t chan;
+ uint8_t sec_id;
+ unsigned server : 1;
+ unsigned init_called : 1;
+};
+
+/* one of these exists for each channel we're dealing with */
+struct fc_channel {
+ struct fc_channel *next;
+ struct fc_client *clients;
+ uint8_t has_server : 1;
+ uint16_t chan;
+};
+
+
+static struct fc_client *fc_clients;
+static struct fc_channel *fc_channels;
+static uint32_t fc_next_id;
+static pthread_once_t fc_init_once = PTHREAD_ONCE_INIT;
+
+
+static void fc_init_work(void)
+{
+ fc_clients = NULL;
+ fc_channels = NULL;
+ fc_next_id = 0;
+
+ //more init here if needed...
+}
+
+static void fc_init(void)
+{
+ pthread_once(&fc_init_once, fc_init_work);
+}
+
+
+static void fcchan_conn_chng_cbk(UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected,
+ UINT16 reason, tBT_TRANSPORT );
+static void fcchan_data_cbk(UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf);
extern void uuid_to_string_legacy(bt_uuid_t *p_uuid, char *str);
@@ -330,16 +382,43 @@ static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pc
}
/*******************************************************************************
- **
- ** Function bta_jv_clear_pm_cb
- **
- ** Description clears jv pm control block and optionally calls bta_sys_conn_close()
- ** In general close_conn should be set to TRUE to remove registering with
- ** dm pm!
- **
- ** WARNING: Make sure to clear pointer form port or l2c to this control block too!
- **
- *******************************************************************************/
+**
+** Function bta_jv_free_l2c_cb
+**
+** Description free the given L2CAP control block
+**
+** Returns
+**
+*******************************************************************************/
+tBTA_JV_STATUS bta_jv_free_l2c_cb(tBTA_JV_L2C_CB *p_cb)
+{
+ tBTA_JV_STATUS status = BTA_JV_SUCCESS;
+
+ if(BTA_JV_ST_NONE != p_cb->state)
+ {
+ bta_jv_free_set_pm_profile_cb((UINT32)p_cb->handle);
+ if (GAP_ConnClose(p_cb->handle) != BT_PASS)
+ status = BTA_JV_FAILURE;
+ }
+ p_cb->psm = 0;
+ p_cb->state = BTA_JV_ST_NONE;
+ bta_jv_free_sec_id(&p_cb->sec_id);
+ p_cb->p_cback = NULL;
+ return status;
+}
+
+/*******************************************************************************
+**
+**
+** Function bta_jv_clear_pm_cb
+**
+** Description clears jv pm control block and optionally calls bta_sys_conn_close()
+** In general close_conn should be set to TRUE to remove registering with
+** dm pm!
+**
+** WARNING: Make sure to clear pointer form port or l2c to this control block too!
+**
+*******************************************************************************/
static void bta_jv_clear_pm_cb(tBTA_JV_PM_CB *p_pm_cb, BOOLEAN close_conn)
{
/* needs to be called if registered with bta pm, otherwise we may run out of dm pm slots! */
@@ -381,10 +460,10 @@ static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle)
appid_counter++;
}
- APPL_TRACE_API("bta_jv_free_set_pm_profile_cb(jv_handle: 0x%2x), idx: %d, "
- "app_id: 0x%x", jv_handle, i, bta_jv_cb.pm_cb[i].app_id);
- APPL_TRACE_API("bta_jv_free_set_pm_profile_cb, bd_counter = %d, "
- "appid_counter = %d", bd_counter, appid_counter);
+ APPL_TRACE_API("%s(jv_handle: 0x%2x), idx: %d, "
+ "app_id: 0x%x",__func__, jv_handle, i, bta_jv_cb.pm_cb[i].app_id);
+ APPL_TRACE_API("%s, bd_counter = %d, "
+ "appid_counter = %d", __func__, bd_counter, appid_counter);
if (bd_counter > 1)
{
bta_jv_pm_conn_idle(&bta_jv_cb.pm_cb[i]);
@@ -410,14 +489,24 @@ static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(UINT32 jv_handle)
if (p_pcb)
{
if (NULL == p_pcb->p_pm_cb)
- APPL_TRACE_WARNING("bta_jv_free_set_pm_profile_cb(jv_handle:"
+ APPL_TRACE_WARNING("%s(jv_handle:"
" 0x%x):port_handle: 0x%x, p_pm_cb: %d: no link to "
- "pm_cb?", jv_handle, p_pcb->port_handle, i);
+ "pm_cb?", __func__, jv_handle, p_pcb->port_handle, i);
p_cb = &p_pcb->p_pm_cb;
}
}
}
-
+ else
+ {
+ if (jv_handle < BTA_JV_MAX_L2C_CONN)
+ {
+ tBTA_JV_L2C_CB *p_l2c_cb = &bta_jv_cb.l2c_cb[jv_handle];
+ if (NULL == p_l2c_cb->p_pm_cb)
+ APPL_TRACE_WARNING("%s(jv_handle: "
+ "0x%x): p_pm_cb: %d: no link to pm_cb?", __func__, jv_handle, i);
+ p_cb = &p_l2c_cb->p_pm_cb;
+ }
+ }
if (p_cb)
{
*p_cb = NULL;
@@ -466,6 +555,23 @@ static tBTA_JV_PM_CB *bta_jv_alloc_set_pm_profile_cb(UINT32 jv_handle, tBTA_JV_P
}
}
}
+ else
+ {
+ /* use jv handle for l2cap bd address retrieval */
+ for (j = 0; j < BTA_JV_MAX_L2C_CONN; j++)
+ {
+ if (jv_handle == bta_jv_cb.l2c_cb[j].handle)
+ {
+ pp_cb = &bta_jv_cb.l2c_cb[j].p_pm_cb;
+ UINT8 *p_bd_addr = GAP_ConnGetRemoteAddr((UINT16)jv_handle);
+ if (NULL != p_bd_addr)
+ bdcpy(peer_bd_addr, p_bd_addr);
+ else
+ i = BTA_JV_PM_MAX_NUM;
+ break;
+ }
+ }
+ }
APPL_TRACE_API("bta_jv_alloc_set_pm_profile_cb(handle 0x%2x, app_id %d): "
"idx: %d, (BTA_JV_PM_MAX_NUM: %d), pp_cb: 0x%x", jv_handle, app_id,
i, BTA_JV_PM_MAX_NUM, pp_cb);
@@ -489,6 +595,72 @@ static tBTA_JV_PM_CB *bta_jv_alloc_set_pm_profile_cb(UINT32 jv_handle, tBTA_JV_P
/*******************************************************************************
**
+** Function bta_jv_check_psm
+**
+** Description for now use only the legal PSM per JSR82 spec
+**
+** Returns TRUE, if allowed
+**
+*******************************************************************************/
+BOOLEAN bta_jv_check_psm(UINT16 psm)
+{
+ BOOLEAN ret = FALSE;
+
+ if (L2C_IS_VALID_PSM(psm))
+ {
+ if (psm < 0x1001)
+ {
+ /* see if this is defined by spec */
+ switch (psm)
+ {
+ case SDP_PSM: /* 1 */
+ case BT_PSM_RFCOMM: /* 3 */
+ /* do not allow java app to use these 2 PSMs */
+ break;
+
+ case TCS_PSM_INTERCOM: /* 5 */
+ case TCS_PSM_CORDLESS: /* 7 */
+ if( FALSE == bta_sys_is_register(BTA_ID_CT) &&
+ FALSE == bta_sys_is_register(BTA_ID_CG) )
+ ret = TRUE;
+ break;
+
+ case BT_PSM_BNEP: /* F */
+ if(FALSE == bta_sys_is_register(BTA_ID_PAN))
+ ret = TRUE;
+ break;
+
+ case HID_PSM_CONTROL: /* 0x11 */
+ case HID_PSM_INTERRUPT: /* 0x13 */
+ //FIX: allow HID Device and HID Host to coexist
+ if( FALSE == bta_sys_is_register(BTA_ID_HD) ||
+ FALSE == bta_sys_is_register(BTA_ID_HH) )
+ ret = TRUE;
+ break;
+
+ case AVCT_PSM: /* 0x17 */
+ case AVDT_PSM: /* 0x19 */
+ if ((FALSE == bta_sys_is_register(BTA_ID_AV)) &&
+ (FALSE == bta_sys_is_register(BTA_ID_AVK)))
+ ret = TRUE;
+ break;
+
+ default:
+ ret = TRUE;
+ break;
+ }
+ }
+ else
+ {
+ ret = TRUE;
+ }
+ }
+ return ret;
+
+}
+
+/*******************************************************************************
+**
** Function bta_jv_enable
**
** Description Initialises the JAVA I/F
@@ -501,6 +673,7 @@ void bta_jv_enable(tBTA_JV_MSG *p_data)
tBTA_JV_STATUS status = BTA_JV_SUCCESS;
bta_jv_cb.p_dm_cback = p_data->enable.p_cback;
bta_jv_cb.p_dm_cback(BTA_JV_ENABLE_EVT, (tBTA_JV *)&status, 0);
+ memset(bta_jv_cb.free_psm_list,0,sizeof(bta_jv_cb.free_psm_list));
}
/*******************************************************************************
@@ -517,9 +690,135 @@ void bta_jv_disable (tBTA_JV_MSG *p_data)
{
UNUSED(p_data);
- APPL_TRACE_ERROR("bta_jv_disable not used");
+ APPL_TRACE_ERROR("%s",__func__);
+}
+
+
+/**
+ * We keep a list of PSM's that have been freed from JAVA, for reuse.
+ * This function will return a free PSM, and delete it from the free
+ * list.
+ * If no free PSMs exist, 0 will be returned.
+ */
+static UINT16 bta_jv_get_free_psm() {
+ const int cnt = sizeof(bta_jv_cb.free_psm_list)/sizeof(bta_jv_cb.free_psm_list[0]);
+ for (int i = 0; i < cnt; i++) {
+ UINT16 psm = bta_jv_cb.free_psm_list[i];
+ if (psm != 0) {
+ APPL_TRACE_DEBUG("%s(): Reusing PSM: 0x%04d", __func__, psm)
+ bta_jv_cb.free_psm_list[i] = 0;
+ return psm;
+ }
+ }
+ return 0;
+}
+
+static void bta_jv_set_free_psm(UINT16 psm) {
+ int free_index = -1;
+ const int cnt = sizeof(bta_jv_cb.free_psm_list)/sizeof(bta_jv_cb.free_psm_list[0]);
+ for (int i = 0; i < cnt; i++) {
+ if (bta_jv_cb.free_psm_list[i] == 0) {
+ free_index = i;
+ } else if (psm == bta_jv_cb.free_psm_list[i]) {
+ return; // PSM already freed?
+ }
+ }
+ if (free_index != -1) {
+ bta_jv_cb.free_psm_list[free_index] = psm;
+ APPL_TRACE_DEBUG("%s(): Recycling PSM: 0x%04d", __func__, psm)
+ } else {
+ APPL_TRACE_ERROR("%s unable to free psm 0x%x no more free slots",__func__, psm);
+ }
}
+/*******************************************************************************
+**
+** Function bta_jv_get_channel_id
+**
+** Description Obtain a free SCN (Server Channel Number)
+** (RFCOMM channel or L2CAP PSM)
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_get_channel_id(tBTA_JV_MSG *p_data)
+{
+ UINT16 psm = 0;
+
+ switch (p_data->alloc_channel.type) {
+ case BTA_JV_CONN_TYPE_RFCOMM: {
+ INT32 channel = p_data->alloc_channel.channel;
+ UINT8 scn = 0;
+ if (channel > 0)
+ {
+ if (BTM_TryAllocateSCN(channel) == FALSE)
+ {
+ APPL_TRACE_ERROR("rfc channel:%d already in use or invalid", channel);
+ channel = 0;
+ }
+ } else if ((channel = BTM_AllocateSCN()) == 0) {
+ APPL_TRACE_ERROR("run out of rfc channels");
+ channel = 0;
+ }
+ if (channel != 0) {
+ bta_jv_cb.scn[channel-1] = TRUE;
+ scn = (UINT8) channel;
+ }
+ if (bta_jv_cb.p_dm_cback)
+ bta_jv_cb.p_dm_cback(BTA_JV_GET_SCN_EVT, (tBTA_JV *)&scn,
+ p_data->alloc_channel.user_data);
+ return;
+ }
+ case BTA_JV_CONN_TYPE_L2CAP:
+ psm = bta_jv_get_free_psm();
+ if (psm == 0) {
+ psm = L2CA_AllocatePSM();
+ APPL_TRACE_DEBUG("%s() returned PSM: 0x%04x", __func__, psm);
+ }
+ break;
+ case BTA_JV_CONN_TYPE_L2CAP_LE:
+ break;
+ default:
+ break;
+ }
+
+ if (bta_jv_cb.p_dm_cback)
+ bta_jv_cb.p_dm_cback(BTA_JV_GET_PSM_EVT, (tBTA_JV *)&psm, p_data->alloc_channel.user_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_free_scn
+**
+** Description free a SCN
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_free_scn(tBTA_JV_MSG *p_data)
+{
+ UINT16 scn = p_data->free_channel.scn;
+
+ switch (p_data->free_channel.type) {
+ case BTA_JV_CONN_TYPE_RFCOMM: {
+ if (scn > 0 && scn <= BTA_JV_MAX_SCN && bta_jv_cb.scn[scn-1])
+ {
+ /* this scn is used by JV */
+ bta_jv_cb.scn[scn-1] = FALSE;
+ BTM_FreeSCN(scn);
+ }
+ break;
+ }
+ case BTA_JV_CONN_TYPE_L2CAP:
+ bta_jv_set_free_psm(scn);
+ break;
+ case BTA_JV_CONN_TYPE_L2CAP_LE:
+ // TODO: Not yet implemented...
+ break;
+ default:
+ break;
+ }
+}
static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID* u)
{
static uint8_t bt_base_uuid[] =
@@ -587,7 +886,7 @@ static void bta_jv_start_discovery_cback(UINT16 result, void * user_data)
logu("shorten uuid:", su.uu.uuid128);
p_sdp_rec = SDP_FindServiceUUIDInDb(p_bta_jv_cfg->p_sdp_db, &su, p_sdp_rec);
APPL_TRACE_DEBUG("p_sdp_rec:%p", p_sdp_rec);
- if(p_sdp_rec && SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ if (p_sdp_rec && SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe))
{
dcomp.scn = (UINT8) pe.params[0];
status = BTA_JV_SUCCESS;
@@ -690,6 +989,450 @@ void bta_jv_delete_record(tBTA_JV_MSG *p_data)
/*******************************************************************************
**
+** Function bta_jv_l2cap_client_cback
+**
+** Description handles the l2cap client events
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_jv_l2cap_client_cback(UINT16 gap_handle, UINT16 event)
+{
+ tBTA_JV_L2C_CB *p_cb = &bta_jv_cb.l2c_cb[gap_handle];
+ tBTA_JV evt_data;
+
+ if (gap_handle >= BTA_JV_MAX_L2C_CONN && !p_cb->p_cback)
+ return;
+
+ APPL_TRACE_DEBUG( "%s: %d evt:x%x",__func__, gap_handle, event);
+ evt_data.l2c_open.status = BTA_JV_SUCCESS;
+ evt_data.l2c_open.handle = gap_handle;
+
+ switch (event)
+ {
+ case GAP_EVT_CONN_OPENED:
+ bdcpy(evt_data.l2c_open.rem_bda, GAP_ConnGetRemoteAddr(gap_handle));
+ evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
+ p_cb->state = BTA_JV_ST_CL_OPEN;
+ p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->user_data);
+ break;
+
+ case GAP_EVT_CONN_CLOSED:
+ p_cb->state = BTA_JV_ST_NONE;
+ bta_jv_free_sec_id(&p_cb->sec_id);
+ evt_data.l2c_close.async = TRUE;
+ p_cb->p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, p_cb->user_data);
+ p_cb->p_cback = NULL;
+ break;
+
+ case GAP_EVT_CONN_DATA_AVAIL:
+ evt_data.data_ind.handle = gap_handle;
+ /* Reset idle timer to avoid requesting sniff mode while receiving data */
+ bta_jv_pm_conn_busy(p_cb->p_pm_cb);
+ p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->user_data);
+ bta_jv_pm_conn_idle(p_cb->p_pm_cb);
+ break;
+
+ case GAP_EVT_CONN_CONGESTED:
+ case GAP_EVT_CONN_UNCONGESTED:
+ p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? TRUE : FALSE;
+ evt_data.l2c_cong.cong = p_cb->cong;
+ p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->user_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_connect
+**
+** Description makes an l2cap client connection
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_connect(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2C_CB *p_cb;
+ tBTA_JV_L2CAP_CL_INIT evt_data;
+ UINT16 handle=GAP_INVALID_HANDLE;
+ UINT8 sec_id;
+ tL2CAP_CFG_INFO cfg;
+ tBTA_JV_API_L2CAP_CONNECT *cc = &(p_data->l2cap_connect);
+ UINT8 chan_mode_mask = GAP_FCR_CHAN_OPT_BASIC;
+ tL2CAP_ERTM_INFO *ertm_info = NULL;
+
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ if (cc->has_cfg == TRUE)
+ {
+ cfg = cc->cfg;
+ if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ chan_mode_mask = GAP_FCR_CHAN_OPT_ERTM;
+ }
+ }
+
+ if (cc->has_ertm_info == TRUE)
+ {
+ ertm_info = &(cc->ertm_info);
+ }
+
+ /* We need to use this value for MTU to be able to handle cases where cfg is not set in req. */
+ cfg.mtu_present = TRUE;
+ cfg.mtu = cc->rx_mtu;
+
+ /* TODO: DM role manager
+ L2CA_SetDesireRole(cc->role);
+ */
+
+ sec_id = bta_jv_alloc_sec_id();
+ evt_data.sec_id = sec_id;
+ evt_data.status = BTA_JV_FAILURE;
+
+ if (sec_id)
+ {
+ if (bta_jv_check_psm(cc->remote_psm)) /* allowed */
+ {
+ if ((handle = GAP_ConnOpen("", sec_id, 0, cc->peer_bd_addr, cc->remote_psm,
+ &cfg, ertm_info, cc->sec_mask, chan_mode_mask,
+ bta_jv_l2cap_client_cback)) != GAP_INVALID_HANDLE )
+ {
+ evt_data.status = BTA_JV_SUCCESS;
+ }
+ }
+ }
+
+ if (evt_data.status == BTA_JV_SUCCESS)
+ {
+ p_cb = &bta_jv_cb.l2c_cb[handle];
+ p_cb->handle = handle;
+ p_cb->p_cback = cc->p_cback;
+ p_cb->user_data = cc->user_data;
+ p_cb->psm = 0; /* not a server */
+ p_cb->sec_id = sec_id;
+ p_cb->state = BTA_JV_ST_CL_OPENING;
+ }
+ else
+ {
+ bta_jv_free_sec_id(&sec_id);
+ }
+
+ evt_data.handle = handle;
+ cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, (tBTA_JV *)&evt_data, cc->user_data);
+}
+
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_close
+**
+** Description Close an L2CAP client connection
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_close(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2CAP_CLOSE evt_data;
+ tBTA_JV_API_L2CAP_CLOSE *cc = &(p_data->l2cap_close);
+ tBTA_JV_L2CAP_CBACK *p_cback = cc->p_cb->p_cback;
+ void *user_data = cc->p_cb->user_data;
+
+ evt_data.handle = cc->handle;
+ evt_data.status = bta_jv_free_l2c_cb(cc->p_cb);
+ evt_data.async = FALSE;
+
+ if (p_cback)
+ p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_server_cback
+**
+** Description handles the l2cap server callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_jv_l2cap_server_cback(UINT16 gap_handle, UINT16 event)
+{
+ tBTA_JV_L2C_CB *p_cb = &bta_jv_cb.l2c_cb[gap_handle];
+ tBTA_JV evt_data;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ void *user_data;
+
+ if (gap_handle >= BTA_JV_MAX_L2C_CONN && !p_cb->p_cback)
+ return;
+
+ APPL_TRACE_DEBUG( "%s: %d evt:x%x", __func__, gap_handle, event);
+ evt_data.l2c_open.status = BTA_JV_SUCCESS;
+ evt_data.l2c_open.handle = gap_handle;
+
+ switch (event)
+ {
+ case GAP_EVT_CONN_OPENED:
+ bdcpy(evt_data.l2c_open.rem_bda, GAP_ConnGetRemoteAddr(gap_handle));
+ evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
+ p_cb->state = BTA_JV_ST_SR_OPEN;
+ p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->user_data);
+ break;
+
+ case GAP_EVT_CONN_CLOSED:
+ evt_data.l2c_close.async = TRUE;
+ evt_data.l2c_close.handle = p_cb->handle;
+ p_cback = p_cb->p_cback;
+ user_data = p_cb->user_data;
+ evt_data.l2c_close.status = bta_jv_free_l2c_cb(p_cb);
+ p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, user_data);
+ break;
+
+ case GAP_EVT_CONN_DATA_AVAIL:
+ evt_data.data_ind.handle = gap_handle;
+ /* Reset idle timer to avoid requesting sniff mode while receiving data */
+ bta_jv_pm_conn_busy(p_cb->p_pm_cb);
+ p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->user_data);
+ bta_jv_pm_conn_idle(p_cb->p_pm_cb);
+ break;
+
+ case GAP_EVT_CONN_CONGESTED:
+ case GAP_EVT_CONN_UNCONGESTED:
+ p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? TRUE : FALSE;
+ evt_data.l2c_cong.cong = p_cb->cong;
+ p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->user_data);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_start_server
+**
+** Description starts an L2CAP server
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_start_server(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2C_CB *p_cb;
+ UINT8 sec_id;
+ UINT16 handle;
+ tL2CAP_CFG_INFO cfg;
+ tBTA_JV_L2CAP_START evt_data;
+ tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server);
+ INT32 use_etm = FALSE;
+ UINT8 chan_mode_mask = GAP_FCR_CHAN_OPT_BASIC;
+ tL2CAP_ERTM_INFO *ertm_info = NULL;
+
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ if (ls->has_cfg == TRUE) {
+ cfg = ls->cfg;
+ if (cfg.fcr_present && cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ chan_mode_mask = GAP_FCR_CHAN_OPT_ERTM;
+ }
+ }
+
+ if (ls->has_ertm_info == TRUE) {
+ ertm_info = &(ls->ertm_info);
+ }
+
+ //FIX: MTU=0 means not present
+ if (ls->rx_mtu >0)
+ {
+ cfg.mtu_present = TRUE;
+ cfg.mtu = ls->rx_mtu;
+ }
+ else
+ {
+ cfg.mtu_present = FALSE;
+ cfg.mtu = 0;
+ }
+
+ /* TODO DM role manager
+ L2CA_SetDesireRole(ls->role);
+ */
+
+ sec_id = bta_jv_alloc_sec_id();
+ if (0 == sec_id || (FALSE == bta_jv_check_psm(ls->local_psm)) ||
+ (handle = GAP_ConnOpen("JV L2CAP", sec_id, 1, 0, ls->local_psm, &cfg, ertm_info,
+ ls->sec_mask, chan_mode_mask, bta_jv_l2cap_server_cback)) == GAP_INVALID_HANDLE)
+ {
+ bta_jv_free_sec_id(&sec_id);
+ evt_data.status = BTA_JV_FAILURE;
+ }
+ else
+ {
+ p_cb = &bta_jv_cb.l2c_cb[handle];
+ evt_data.status = BTA_JV_SUCCESS;
+ evt_data.handle = handle;
+ evt_data.sec_id = sec_id;
+ p_cb->p_cback = ls->p_cback;
+ p_cb->user_data = ls->user_data;
+ p_cb->handle = handle;
+ p_cb->sec_id = sec_id;
+ p_cb->state = BTA_JV_ST_SR_LISTEN;
+ p_cb->psm = ls->local_psm;
+ }
+
+ ls->p_cback(BTA_JV_L2CAP_START_EVT, (tBTA_JV *)&evt_data, ls->user_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_stop_server
+**
+** Description stops an L2CAP server
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_stop_server(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2C_CB *p_cb;
+ tBTA_JV_L2CAP_CLOSE evt_data;
+ tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server);
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ void *user_data;
+ for (int i = 0; i < BTA_JV_MAX_L2C_CONN; i++)
+ {
+ if (bta_jv_cb.l2c_cb[i].psm == ls->local_psm)
+ {
+ p_cb = &bta_jv_cb.l2c_cb[i];
+ p_cback = p_cb->p_cback;
+ user_data = p_cb->user_data;
+ evt_data.handle = p_cb->handle;
+ evt_data.status = bta_jv_free_l2c_cb(p_cb);
+ evt_data.async = FALSE;
+ p_cback(BTA_JV_L2CAP_CLOSE_EVT, (tBTA_JV *)&evt_data, user_data);
+ break;
+ }
+ }
+}
+
+
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_read
+**
+** Description Read data from an L2CAP connection
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_read(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2CAP_READ evt_data;
+ tBTA_JV_API_L2CAP_READ *rc = &(p_data->l2cap_read);
+
+ evt_data.status = BTA_JV_FAILURE;
+ evt_data.handle = rc->handle;
+ evt_data.req_id = rc->req_id;
+ evt_data.p_data = rc->p_data;
+ evt_data.len = 0;
+
+ if (BT_PASS == GAP_ConnReadData(rc->handle, rc->p_data, rc->len, &evt_data.len))
+ {
+ evt_data.status = BTA_JV_SUCCESS;
+ }
+
+ rc->p_cback(BTA_JV_L2CAP_READ_EVT, (tBTA_JV *)&evt_data, rc->user_data);
+}
+
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_write
+**
+** Description Write data to an L2CAP connection
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_write(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2CAP_WRITE evt_data;
+ tBTA_JV_API_L2CAP_WRITE *ls = &(p_data->l2cap_write);
+
+ /* As we check this callback exists before the tBTA_JV_API_L2CAP_WRITE can be send through the
+ * API this check should not be needed.
+ * But the API is not designed to be used (safely at least) in a multi-threaded scheduler, hence
+ * if the peer device disconnects the l2cap link after the API is called, but before this
+ * message is handled, the ->p_cback will be cleared at this point. At first glanch this seems
+ * highly unlikely, but for all obex-profiles with two channels connected - e.g. MAP, this
+ * happens around 1 of 4 disconnects, as a disconnect on the server channel causes a disconnect
+ * to be send on the client (notification) channel, but at the peer typically disconnects both
+ * the OBEX disconnect request crosses the incoming l2cap disconnect.
+ * If p_cback is cleared, we simply discard the data.
+ * RISK: The caller must handle any cleanup based on another signal than BTA_JV_L2CAP_WRITE_EVT,
+ * which is typically not possible, as the pointer to the allocated buffer is stored
+ * in this message, and can therefore not be freed, hence we have a mem-leak-by-design.*/
+ if (ls->p_cb->p_cback != NULL) {
+ evt_data.status = BTA_JV_FAILURE;
+ evt_data.handle = ls->handle;
+ evt_data.req_id = ls->req_id;
+ evt_data.cong = ls->p_cb->cong;
+ evt_data.len = 0;
+ bta_jv_pm_conn_busy(ls->p_cb->p_pm_cb);
+ if (!evt_data.cong &&
+ BT_PASS == GAP_ConnWriteData(ls->handle, ls->p_data, ls->len, &evt_data.len))
+ {
+ evt_data.status = BTA_JV_SUCCESS;
+ }
+ ls->p_cb->p_cback(BTA_JV_L2CAP_WRITE_EVT, (tBTA_JV *)&evt_data, ls->user_data);
+ bta_jv_set_pm_conn_state(ls->p_cb->p_pm_cb, BTA_JV_CONN_IDLE);
+ } else {
+ /* As this pointer is checked in the API function, this occurs only when the channel is
+ * disconnected after the API function is called, but before the message is handled. */
+ APPL_TRACE_ERROR("%s() ls->p_cb->p_cback == NULL", __func__);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_write_fixed
+**
+** Description Write data to an L2CAP connection using Fixed channels
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_write_fixed(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_L2CAP_WRITE_FIXED evt_data;
+ tBTA_JV_API_L2CAP_WRITE_FIXED *ls = &(p_data->l2cap_write_fixed);
+ BT_HDR *msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + ls->len + L2CAP_MIN_OFFSET);
+ if (!msg)
+ {
+ APPL_TRACE_ERROR("%s() could not allocate msg buffer",__func__);
+ return;
+ }
+ evt_data.status = BTA_JV_FAILURE;
+ evt_data.channel = ls->channel;
+ memcpy(evt_data.addr, ls->addr, sizeof(evt_data.addr));
+ evt_data.req_id = ls->req_id;
+ evt_data.len = 0;
+
+
+ memcpy(((uint8_t*)(msg + 1)) + L2CAP_MIN_OFFSET, ls->p_data, ls->len);
+ msg->len = ls->len;
+ msg->offset = L2CAP_MIN_OFFSET;
+
+ L2CA_SendFixedChnlData(ls->channel, ls->addr, msg);
+
+ ls->p_cback(BTA_JV_L2CAP_WRITE_FIXED_EVT, (tBTA_JV *)&evt_data, ls->user_data);
+}
+
+/*******************************************************************************
+**
** Function bta_jv_port_data_co_cback
**
** Description port data callback function of rfcomm
@@ -703,8 +1446,7 @@ static int bta_jv_port_data_co_cback(UINT16 port_handle, UINT8 *buf, UINT16 len,
tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle);
tBTA_JV_PCB *p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
int ret = 0;
- APPL_TRACE_DEBUG("bta_jv_port_data_co_cback, p_cb:%p, p_pcb:%p, len:%d, type:%d",
- p_cb, p_pcb, len, type);
+ APPL_TRACE_DEBUG("%s, p_cb:%p, p_pcb:%p, len:%d, type:%d", __func__, p_cb, p_pcb, len, type);
if (p_pcb != NULL)
{
switch(type)
@@ -797,7 +1539,7 @@ static void bta_jv_port_event_cl_cback(UINT32 code, UINT16 port_handle)
tBTA_JV evt_data;
APPL_TRACE_DEBUG( "bta_jv_port_event_cl_cback:%d", port_handle);
- if(NULL == p_cb || NULL == p_cb->p_cback)
+ if (NULL == p_cb || NULL == p_cb->p_cback)
return;
APPL_TRACE_DEBUG( "bta_jv_port_event_cl_cback code=x%x port_handle:%d handle:%d",
@@ -961,6 +1703,38 @@ void bta_jv_rfcomm_close(tBTA_JV_MSG *p_data)
/*******************************************************************************
**
+** Function bta_jv_get_num_rfc_listen
+**
+** Description when a RFCOMM connection goes down, make sure that there's only
+** one port stays listening on this scn.
+**
+** Returns
+**
+*******************************************************************************/
+static UINT8 bta_jv_get_num_rfc_listen(tBTA_JV_RFC_CB *p_cb)
+{
+ UINT8 listen=1;
+
+ if (p_cb->max_sess > 1)
+ {
+ listen = 0;
+ for (UINT8 i=0; i<p_cb->max_sess; i++)
+ {
+ if (p_cb->rfc_hdl[i] != 0)
+ {
+ const tBTA_JV_PCB *p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[i] - 1];
+ if (BTA_JV_ST_SR_LISTEN == p_pcb->state)
+ {
+ listen++;
+ }
+ }
+ }
+ }
+ return listen;
+}
+
+/*******************************************************************************
+**
** Function bta_jv_port_mgmt_sr_cback
**
** Description callback for port mamangement function of rfcomm
@@ -979,7 +1753,7 @@ static void bta_jv_port_mgmt_sr_cback(UINT32 code, UINT16 port_handle)
UINT8 num;
UINT32 si;
APPL_TRACE_DEBUG("bta_jv_port_mgmt_sr_cback, code:%d, port_handle:%d", code, port_handle);
- if(NULL == p_cb || NULL == p_cb->p_cback)
+ if (NULL == p_cb || NULL == p_cb->p_cback)
{
APPL_TRACE_ERROR("bta_jv_port_mgmt_sr_cback, p_cb:%p, p_cb->p_cback%p",
p_cb, p_cb ? p_cb->p_cback : NULL);
@@ -1018,7 +1792,7 @@ static void bta_jv_port_mgmt_sr_cback(UINT32 code, UINT16 port_handle)
tBTA_JV_RFCOMM_CBACK *p_cback = p_cb->p_cback;
APPL_TRACE_DEBUG("PORT_CLOSED before BTA_JV_RFCOMM_CLOSE_EVT: curr_sess:%d, max_sess:%d",
p_cb->curr_sess, p_cb->max_sess);
- if(BTA_JV_ST_SR_CLOSING == p_pcb->state)
+ if (BTA_JV_ST_SR_CLOSING == p_pcb->state)
{
evt_data.rfc_close.async = FALSE;
evt_data.rfc_close.status = BTA_JV_SUCCESS;
@@ -1047,7 +1821,7 @@ static void bta_jv_port_event_sr_cback(UINT32 code, UINT16 port_handle)
tBTA_JV_RFC_CB *p_cb = bta_jv_rfc_port_to_cb(port_handle);
tBTA_JV evt_data;
- if(NULL == p_cb || NULL == p_cb->p_cback)
+ if (NULL == p_cb || NULL == p_cb->p_cback)
return;
APPL_TRACE_DEBUG( "bta_jv_port_event_sr_cback code=x%x port_handle:%d handle:%d",
@@ -1101,7 +1875,7 @@ static tBTA_JV_PCB * bta_jv_add_rfc_port(tBTA_JV_RFC_CB *p_cb, tBTA_JV_PCB *p_pc
if (p_pcb->state == BTA_JV_ST_SR_LISTEN)
{
listen++;
- if(p_pcb_open == p_pcb)
+ if (p_pcb_open == p_pcb)
{
APPL_TRACE_DEBUG("bta_jv_add_rfc_port, port_handle:%d, change the listen port to open state",
p_pcb->port_handle);
@@ -1208,7 +1982,7 @@ void bta_jv_rfcomm_start_server(tBTA_JV_MSG *p_data)
p_cb = bta_jv_alloc_rfc_cb(handle, &p_pcb);
- if(!p_cb)
+ if (!p_cb)
{
APPL_TRACE_ERROR("bta_jv_rfcomm_start_server, run out of rfc control block");
break;
@@ -1236,15 +2010,15 @@ void bta_jv_rfcomm_start_server(tBTA_JV_MSG *p_data)
} while (0);
rs->p_cback(BTA_JV_RFCOMM_START_EVT, (tBTA_JV *)&evt_data, rs->user_data);
- if(evt_data.status == BTA_JV_SUCCESS)
+ if (evt_data.status == BTA_JV_SUCCESS)
{
PORT_SetDataCOCallback (handle, bta_jv_port_data_co_cback);
}
else
{
- if(sec_id)
+ if (sec_id)
bta_jv_free_sec_id(&sec_id);
- if(handle)
+ if (handle)
RFCOMM_RemoveConnection(handle);
}
}
@@ -1265,13 +2039,13 @@ void bta_jv_rfcomm_stop_server(tBTA_JV_MSG *p_data)
tBTA_JV_RFC_CB *p_cb = NULL;
tBTA_JV_PCB *p_pcb = NULL;
APPL_TRACE_DEBUG("bta_jv_rfcomm_stop_server");
- if(!ls->handle)
+ if (!ls->handle)
{
APPL_TRACE_ERROR("bta_jv_rfcomm_stop_server, jv handle is null");
return;
}
void* user_data = ls->user_data;
- if(!find_rfc_pcb(user_data, &p_cb, &p_pcb))
+ if (!find_rfc_pcb(user_data, &p_cb, &p_pcb))
return;
APPL_TRACE_DEBUG("bta_jv_rfcomm_stop_server: p_pcb:%p, p_pcb->port_handle:%d",
p_pcb, p_pcb->port_handle);
@@ -1535,3 +2309,420 @@ static void bta_jv_pm_state_change(tBTA_JV_PM_CB *p_cb, const tBTA_JV_CONN_STATE
break;
}
}
+/**********************************************************************************************/
+
+
+static struct fc_channel *fcchan_get(uint16_t chan, char create)
+{
+ struct fc_channel *t = fc_channels;
+ static tL2CAP_FIXED_CHNL_REG fcr = {
+ .pL2CA_FixedConn_Cb = fcchan_conn_chng_cbk,
+ .pL2CA_FixedData_Cb = fcchan_data_cbk,
+ .default_idle_tout = 0xffff,
+ .fixed_chnl_opts = {
+ .mode = L2CAP_FCR_BASIC_MODE,
+ .max_transmit = 0xFF,
+ .rtrans_tout = 2000,
+ .mon_tout = 12000,
+ .mps = 670,
+ .tx_win_sz = 1,
+ },
+ };
+
+ while (t && t->chan != chan)
+ t = t->next;
+
+ if (t)
+ return t;
+ else if (!create)
+ return NULL; /* we cannot alloc a struct if not asked to */
+
+ t = calloc(sizeof(*t), 1);
+ if (!t)
+ return NULL;
+
+ t->chan = chan;
+
+ if (!L2CA_RegisterFixedChannel(chan, &fcr)) {
+ free(t);
+ return NULL;
+ }
+
+ //link it in
+ t->next = fc_channels;
+ fc_channels = t;
+
+ return t;
+}
+
+/* pass NULL to find servers */
+static struct fc_client *fcclient_find_by_addr(struct fc_client *start, BD_ADDR addr)
+{
+ struct fc_client *t = start;
+
+ while (t) {
+
+ /* match client if have addr */
+ if (addr && !memcmp(addr, &t->remote_addr, sizeof(t->remote_addr)))
+ break;
+
+ /* match server if do not have addr */
+ if (!addr && t->server)
+ break;
+
+ t = t->next_all_list;
+ }
+
+ return t;
+}
+
+static struct fc_client *fcclient_find_by_id(uint32_t id)
+{
+ struct fc_client *t = fc_clients;
+
+ while (t && t->id != id)
+ t = t->next_all_list;
+
+ return t;
+}
+
+static struct fc_client *fcclient_alloc(uint16_t chan, char server, const uint8_t *sec_id_to_use)
+{
+ struct fc_channel *fc = fcchan_get(chan, TRUE);
+ struct fc_client *t;
+ uint8_t sec_id;
+
+ if (!fc)
+ return NULL;
+
+ if (fc->has_server && server)
+ return NULL; /* no way to have multiple servers on same channel */
+
+ if (sec_id_to_use)
+ sec_id = *sec_id_to_use;
+ else
+ sec_id = bta_jv_alloc_sec_id();
+
+ t = calloc(sizeof(*t), 1);
+ if (t) {
+ //allocate it a unique ID
+ do {
+ t->id = ++fc_next_id;
+ } while (!t->id || fcclient_find_by_id(t->id));
+
+ //populate some params
+ t->chan = chan;
+ t->server = server;
+
+ //get a security id
+ t->sec_id = sec_id;
+
+ //link it in to global list
+ t->next_all_list = fc_clients;
+ fc_clients = t;
+
+ //link it in to channel list
+ t->next_chan_list = fc->clients;
+ fc->clients = t;
+
+ //update channel if needed
+ if (server)
+ fc->has_server = TRUE;
+ } else if (!sec_id_to_use)
+ bta_jv_free_sec_id(&sec_id);
+
+ return t;
+}
+
+static void fcclient_free(struct fc_client *fc)
+{
+ struct fc_client *t = fc_clients;
+ struct fc_channel *tc = fcchan_get(fc->chan, FALSE);
+
+ //remove from global list
+ while (t && t->next_all_list != fc)
+ t = t->next_all_list;
+
+ if (!t && fc != fc_clients)
+ return; /* prevent double-free */
+
+ if (t)
+ t->next_all_list = fc->next_all_list;
+ else
+ fc_clients = fc->next_all_list;
+
+ //remove from channel list
+ if (tc) {
+ t = tc->clients;
+
+ while (t && t->next_chan_list != fc)
+ t = t->next_chan_list;
+
+ if (t)
+ t->next_chan_list = fc->next_chan_list;
+ else
+ tc->clients = fc->next_chan_list;
+
+ //if was server then channel no longer has a server
+ if (fc->server)
+ tc->has_server = FALSE;
+ }
+
+ //free security id
+ bta_jv_free_sec_id(&fc->sec_id);
+
+ free(fc);
+}
+
+static void fcchan_conn_chng_cbk(UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport)
+{
+ tBTA_JV init_evt;
+ tBTA_JV open_evt;
+ struct fc_channel *tc;
+ struct fc_client *t = NULL, *new_conn;
+ tBTA_JV_L2CAP_CBACK *p_cback = NULL;
+ char call_init = FALSE;
+ void *user_data = NULL;
+
+
+ tc = fcchan_get(chan, FALSE);
+ if (tc) {
+ t = fcclient_find_by_addr(tc->clients, bd_addr); // try to find an open socked for that addr
+ if (t) {
+ p_cback = t->p_cback;
+ user_data = t->user_data;
+ } else {
+ t = fcclient_find_by_addr(tc->clients, NULL); // try to find a listening socked for that channel
+ if (t) {
+ //found: create a normal connection socket and assign the connection to it
+ new_conn = fcclient_alloc(chan, FALSE, &t->sec_id);
+ if (new_conn){
+
+ memcpy(&new_conn->remote_addr, bd_addr, sizeof(new_conn->remote_addr));
+ new_conn->p_cback = NULL; //for now
+ new_conn->init_called = TRUE; /*nop need to do it again */
+
+ p_cback = t->p_cback;
+ user_data = t->user_data;
+
+ t = new_conn;
+ }
+ } else {
+ //drop it
+ return;
+ }
+ }
+ }
+
+ if (t) {
+
+ if (!t->init_called) {
+
+ call_init = TRUE;
+ t->init_called = TRUE;
+
+ init_evt.l2c_cl_init.handle = t->id;
+ init_evt.l2c_cl_init.status = BTA_JV_SUCCESS;
+ init_evt.l2c_cl_init.sec_id = t->sec_id;
+ }
+
+ open_evt.l2c_open.handle = t->id;
+ open_evt.l2c_open.tx_mtu = 23; /* 23, why not ?*/
+ memcpy(&open_evt.l2c_le_open.rem_bda, &t->remote_addr, sizeof(open_evt.l2c_le_open.rem_bda));
+ open_evt.l2c_le_open.p_p_cback = (void**)&t->p_cback;
+ open_evt.l2c_le_open.p_user_data = &t->user_data;
+ open_evt.l2c_le_open.status = BTA_JV_SUCCESS;
+
+ if (connected) {
+ open_evt.l2c_open.status = BTA_JV_SUCCESS;
+ } else {
+ fcclient_free(t);
+ open_evt.l2c_open.status = BTA_JV_FAILURE;
+ }
+ }
+
+ if (call_init)
+ p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &init_evt, user_data);
+
+ //call this with lock taken so socket does not disappear from under us */
+ if (p_cback) {
+ p_cback(BTA_JV_L2CAP_OPEN_EVT, &open_evt, user_data);
+ if (!t->p_cback) /* no callback set, means they do not want this one... */
+ fcclient_free(t);
+ }
+}
+
+static void fcchan_data_cbk(UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf)
+{
+ tBTA_JV evt_data;
+ tBTA_JV evt_open;
+ struct fc_channel *tc;
+ struct fc_client *t = NULL;
+ tBTA_JV_L2CAP_CBACK *sock_cback = NULL;
+ void *sock_user_data;
+
+ tc = fcchan_get(chan, FALSE);
+ if (tc) {
+ t = fcclient_find_by_addr(tc->clients, bd_addr); // try to find an open socked for that addr and channel
+ if (!t) {
+ //no socket -> drop it
+ return;
+ }
+ }
+
+ sock_cback = t->p_cback;
+ sock_user_data = t->user_data;
+ evt_data.le_data_ind.handle = t->id;
+ evt_data.le_data_ind.p_buf = p_buf;
+
+ if (sock_cback)
+ sock_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, sock_user_data);
+}
+
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_connect_le
+**
+** Description makes an le l2cap client connection
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_connect_le(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_API_L2CAP_CONNECT *cc = &(p_data->l2cap_connect);
+ tBTA_JV evt;
+ uint32_t id;
+ char call_init_f = TRUE;
+ struct fc_client *t;
+
+ evt.l2c_cl_init.handle = GAP_INVALID_HANDLE;
+ evt.l2c_cl_init.status = BTA_JV_FAILURE;
+
+ t = fcclient_alloc(cc->remote_chan, FALSE, NULL);
+ if (!t) {
+ cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &evt, cc->user_data);
+ return;
+ }
+
+ t->p_cback = cc->p_cback;
+ t->user_data = cc->user_data;
+ memcpy(&t->remote_addr, &cc->peer_bd_addr, sizeof(t->remote_addr));
+ id = t->id;
+ t->init_called = FALSE;
+
+ if (L2CA_ConnectFixedChnl(t->chan, t->remote_addr)) {
+
+ evt.l2c_cl_init.status = BTA_JV_SUCCESS;
+ evt.l2c_cl_init.handle = id;
+ }
+
+ //it could have been deleted/moved from under us, so re-find it */
+ t = fcclient_find_by_id(id);
+ if (t) {
+ if (evt.l2c_cl_init.status == BTA_JV_SUCCESS)
+ call_init_f = !t->init_called;
+ else
+ fcclient_free(t);
+ }
+ if (call_init_f)
+ cc->p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &evt, cc->user_data);
+ t->init_called = TRUE;
+}
+
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_stop_server_le
+**
+** Description stops an LE L2CAP server
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_stop_server_le(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV evt;
+ tBTA_JV_API_L2CAP_SERVER *ls = &(p_data->l2cap_server);
+ tBTA_JV_L2CAP_CBACK *p_cback = NULL;
+ struct fc_channel *fcchan;
+ struct fc_client *fcclient;
+ void *user_data;
+
+ evt.l2c_close.status = BTA_JV_FAILURE;
+ evt.l2c_close.async = FALSE;
+ evt.l2c_close.handle = GAP_INVALID_HANDLE;
+
+ fcchan = fcchan_get(ls->local_chan, FALSE);
+ if (fcchan) {
+ while((fcclient = fcchan->clients)) {
+ p_cback = fcclient->p_cback;
+ user_data = fcclient->user_data;
+
+ evt.l2c_close.handle = fcclient->id;
+ evt.l2c_close.status = BTA_JV_SUCCESS;
+ evt.l2c_close.async = FALSE;
+
+ fcclient_free(fcclient);
+
+ if (p_cback)
+ p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt, user_data);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_start_server_le
+**
+** Description starts an LE L2CAP server
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_jv_l2cap_start_server_le(tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_API_L2CAP_SERVER *ss = &(p_data->l2cap_server);
+ tBTA_JV_L2CAP_START evt_data;
+ struct fc_client *t;
+ uint16_t handle;
+
+ evt_data.handle = GAP_INVALID_HANDLE;
+ evt_data.status = BTA_JV_FAILURE;
+
+
+ t = fcclient_alloc(ss->local_chan, TRUE, NULL);
+ if (!t)
+ goto out;
+
+ t->p_cback = ss->p_cback;
+ t->user_data = ss->user_data;
+
+ //if we got here, we're registered...
+ evt_data.status = BTA_JV_SUCCESS;
+ evt_data.handle = t->id;
+ evt_data.sec_id = t->sec_id;
+
+out:
+ ss->p_cback(BTA_JV_L2CAP_START_EVT, (tBTA_JV *)&evt_data, ss->user_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_jv_l2cap_close_fixed
+**
+** Description close a fixed channel connection. calls no callbacks. idempotent
+**
+** Returns void
+**
+*******************************************************************************/
+extern void bta_jv_l2cap_close_fixed (tBTA_JV_MSG *p_data)
+{
+ tBTA_JV_API_L2CAP_CLOSE *cc = &(p_data->l2cap_close);
+ struct fc_client *t;
+
+ t = fcclient_find_by_id(cc->handle);
+ if (t)
+ fcclient_free(t);
+}
diff --git a/bta/jv/bta_jv_api.c b/bta/jv/bta_jv_api.c
index a242f455b..41506cdee 100644
--- a/bta/jv/bta_jv_api.c
+++ b/bta/jv/bta_jv_api.c
@@ -31,6 +31,7 @@
#include "port_api.h"
#include "sdp_api.h"
#include "utl.h"
+#include "gap_api.h"
/*****************************************************************************
** Constants
@@ -150,6 +151,78 @@ BOOLEAN BTA_JvIsEncrypted(BD_ADDR bd_addr)
}
return is_encrypted;
}
+/*******************************************************************************
+**
+** Function BTA_JvGetChannelId
+**
+** Description This function reserves a SCN (server channel number) for
+** applications running over RFCOMM, L2CAP of L2CAP_LE.
+** It is primarily called by server profiles/applications to
+** register their SCN into the SDP database. The SCN is reported
+** by the tBTA_JV_DM_CBACK callback with a BTA_JV_GET_SCN_EVT
+** for RFCOMM channels and BTA_JV_GET_PSM_EVT for L2CAP and LE.
+** If the SCN/PSM reported is 0, that means all resources are
+** exhausted.
+** Parameters
+** conn_type one of BTA_JV_CONN_TYPE_
+** user_data Any uservalue - will be returned in the resulting event.
+** channel Only used for RFCOMM - to try to allocate a specific RFCOMM
+** channel.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvGetChannelId(int conn_type, void* user_data, INT32 channel)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_ALLOC_CHANNEL *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+ if ((p_msg = (tBTA_JV_API_ALLOC_CHANNEL *)GKI_getbuf(sizeof(tBTA_JV_API_ALLOC_CHANNEL))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_GET_CHANNEL_EVT;
+ p_msg->type = conn_type;
+ p_msg->channel = channel;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvFreeChannel
+**
+** Description This function frees a server channel number that was used
+** by an application running over RFCOMM.
+** Parameters
+** channel The channel to free
+** conn_type one of BTA_JV_CONN_TYPE_
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvFreeChannel(UINT16 channel, int conn_type)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_FREE_CHANNEL *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+ if ((p_msg = (tBTA_JV_API_FREE_CHANNEL *)GKI_getbuf(sizeof(tBTA_JV_API_FREE_CHANNEL))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_FREE_SCN_EVT;
+ p_msg->scn = channel;
+ p_msg->type = conn_type;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
/*******************************************************************************
**
@@ -243,6 +316,537 @@ tBTA_JV_STATUS BTA_JvDeleteRecord(UINT32 handle)
/*******************************************************************************
**
+** Function BTA_JvL2capConnectLE
+**
+** Description Initiate an LE connection as a L2CAP client to the given BD
+** Address.
+** When the connection is initiated or failed to initiate,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT
+** When the connection is established or failed,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capConnectLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_chan,
+ UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_CONNECT *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (p_cback &&
+ (p_msg =
+ (tBTA_JV_API_L2CAP_CONNECT *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_CONNECT))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_CONNECT_LE_EVT;
+ p_msg->sec_mask = sec_mask;
+ p_msg->role = role;
+ p_msg->remote_chan = remote_chan;
+ p_msg->rx_mtu = rx_mtu;
+ if(cfg != NULL) {
+ p_msg->has_cfg = TRUE;
+ p_msg->cfg = *cfg;
+ } else {
+ p_msg->has_cfg = FALSE;
+ }
+ if(ertm_info != NULL) {
+ p_msg->has_ertm_info = TRUE;
+ p_msg->ertm_info = *ertm_info;
+ } else {
+ p_msg->has_ertm_info = FALSE;
+ }
+ memcpy(p_msg->peer_bd_addr, peer_bd_addr, sizeof(BD_ADDR));
+ p_msg->p_cback = p_cback;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capConnect
+**
+** Description Initiate a connection as a L2CAP client to the given BD
+** Address.
+** When the connection is initiated or failed to initiate,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_CL_INIT_EVT
+** When the connection is established or failed,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capConnect(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info, UINT16 remote_psm,
+ UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ BD_ADDR peer_bd_addr, tBTA_JV_L2CAP_CBACK *p_cback, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_CONNECT *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (p_cback &&
+ (p_msg = (tBTA_JV_API_L2CAP_CONNECT *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_CONNECT))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_CONNECT_EVT;
+ p_msg->sec_mask = sec_mask;
+ p_msg->role = role;
+ p_msg->remote_psm = remote_psm;
+ p_msg->rx_mtu = rx_mtu;
+ if(cfg != NULL) {
+ p_msg->has_cfg = TRUE;
+ p_msg->cfg = *cfg;
+ } else {
+ p_msg->has_cfg = FALSE;
+ }
+ if(ertm_info != NULL) {
+ p_msg->has_ertm_info = TRUE;
+ p_msg->ertm_info = *ertm_info;
+ } else {
+ p_msg->has_ertm_info = FALSE;
+ }
+ memcpy(p_msg->peer_bd_addr, peer_bd_addr, sizeof(BD_ADDR));
+ p_msg->p_cback = p_cback;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capClose
+**
+** Description This function closes an L2CAP client connection
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capClose(UINT32 handle)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_CLOSE *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback &&
+ (p_msg = (tBTA_JV_API_L2CAP_CLOSE *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_CLOSE))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_CLOSE_EVT;
+ p_msg->handle = handle;
+ p_msg->p_cb = &bta_jv_cb.l2c_cb[handle];
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capCloseLE
+**
+** Description This function closes an L2CAP client connection for Fixed Channels
+** Function is idempotent and no callbacks are called!
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capCloseLE(UINT32 handle)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_CLOSE *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if ((p_msg = (tBTA_JV_API_L2CAP_CLOSE *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_CLOSE))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_CLOSE_FIXED_EVT;
+ p_msg->handle = handle;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStartServer
+**
+** Description This function starts an L2CAP server and listens for an L2CAP
+** connection from a remote Bluetooth device. When the server
+** is started successfully, tBTA_JV_L2CAP_CBACK is called with
+** BTA_JV_L2CAP_START_EVT. When the connection is established,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capStartServer(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info,UINT16 local_psm, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ tBTA_JV_L2CAP_CBACK *p_cback, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_SERVER *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (p_cback &&
+ (p_msg = (tBTA_JV_API_L2CAP_SERVER *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_START_SERVER_EVT;
+ p_msg->sec_mask = sec_mask;
+ p_msg->role = role;
+ p_msg->local_psm = local_psm;
+ p_msg->rx_mtu = rx_mtu;
+ if(cfg != NULL) {
+ p_msg->has_cfg = TRUE;
+ p_msg->cfg = *cfg;
+ } else {
+ p_msg->has_cfg = FALSE;
+ }
+ if(ertm_info != NULL) {
+ p_msg->has_ertm_info = TRUE;
+ p_msg->ertm_info = *ertm_info;
+ } else {
+ p_msg->has_ertm_info = FALSE;
+ }
+ p_msg->p_cback = p_cback;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStartServerLE
+**
+** Description This function starts an LE L2CAP server and listens for an L2CAP
+** connection from a remote Bluetooth device. When the server
+** is started successfully, tBTA_JV_L2CAP_CBACK is called with
+** BTA_JV_L2CAP_START_EVT. When the connection is established,
+** tBTA_JV_L2CAP_CBACK is called with BTA_JV_L2CAP_OPEN_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capStartServerLE(tBTA_SEC sec_mask, tBTA_JV_ROLE role,
+ const tL2CAP_ERTM_INFO *ertm_info,UINT16 local_chan, UINT16 rx_mtu, tL2CAP_CFG_INFO *cfg,
+ tBTA_JV_L2CAP_CBACK *p_cback, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_SERVER *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (p_cback &&
+ (p_msg = (tBTA_JV_API_L2CAP_SERVER *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_START_SERVER_LE_EVT;
+ p_msg->sec_mask = sec_mask;
+ p_msg->role = role;
+ p_msg->local_chan = local_chan;
+ p_msg->rx_mtu = rx_mtu;
+ if(cfg != NULL) {
+ p_msg->has_cfg = TRUE;
+ p_msg->cfg = *cfg;
+ } else {
+ p_msg->has_cfg = FALSE;
+ }
+ if(ertm_info != NULL) {
+ p_msg->has_ertm_info = TRUE;
+ p_msg->ertm_info = *ertm_info;
+ } else {
+ p_msg->has_ertm_info = FALSE;
+ }
+ p_msg->p_cback = p_cback;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStopServer
+**
+** Description This function stops the L2CAP server. If the server has an
+** active connection, it would be closed.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capStopServer(UINT16 local_psm, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_SERVER *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if ((p_msg = (tBTA_JV_API_L2CAP_SERVER *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_STOP_SERVER_EVT;
+ p_msg->local_psm = local_psm;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capStopServerLE
+**
+** Description This function stops the LE L2CAP server. If the server has an
+** active connection, it would be closed.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capStopServerLE(UINT16 local_chan, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_SERVER *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if ((p_msg = (tBTA_JV_API_L2CAP_SERVER *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_SERVER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT;
+ p_msg->local_chan = local_chan;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capRead
+**
+** Description This function reads data from an L2CAP connecti;
+ tBTA_JV_RFC_CB *p_cb = rc->p_cb;
+on
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_READ_EVT.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capRead(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_L2CAP_READ evt_data;
+
+ APPL_TRACE_API( "%s", __func__);
+
+
+ if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback)
+ {
+ status = BTA_JV_SUCCESS;
+ evt_data.status = BTA_JV_FAILURE;
+ evt_data.handle = handle;
+ evt_data.req_id = req_id;
+ evt_data.p_data = p_data;
+ evt_data.len = 0;
+
+ if (BT_PASS == GAP_ConnReadData((UINT16)handle, p_data, len, &evt_data.len))
+ {
+ evt_data.status = BTA_JV_SUCCESS;
+ }
+ bta_jv_cb.l2c_cb[handle].p_cback(
+ BTA_JV_L2CAP_READ_EVT, (tBTA_JV *)&evt_data, bta_jv_cb.l2c_cb[handle].user_data);
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capReceive
+**
+** Description This function reads data from an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_RECEIVE_EVT.
+** If there are more data queued in L2CAP than len, the extra data will be discarded.
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capReceive(UINT32 handle, UINT32 req_id, UINT8 *p_data, UINT16 len)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_L2CAP_RECEIVE evt_data;
+ UINT32 left_over = 0;
+ UINT16 max_len, read_len;
+
+ APPL_TRACE_API( "%s", __func__);
+
+
+ if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback)
+ {
+ status = BTA_JV_SUCCESS;
+ evt_data.status = BTA_JV_FAILURE;
+ evt_data.handle = handle;
+ evt_data.req_id = req_id;
+ evt_data.p_data = p_data;
+ evt_data.len = 0;
+
+ if (BT_PASS == GAP_ConnReadData((UINT16)handle, p_data, len, &evt_data.len))
+ {
+ evt_data.status = BTA_JV_SUCCESS;
+ GAP_GetRxQueueCnt ((UINT16)handle, &left_over);
+ while (left_over)
+ {
+ max_len = (left_over > 0xFFFF)?0xFFFF:left_over;
+ GAP_ConnReadData ((UINT16)handle, NULL, max_len, &read_len);
+ left_over -= read_len;
+ }
+ }
+ bta_jv_cb.l2c_cb[handle].p_cback(
+ BTA_JV_L2CAP_RECEIVE_EVT, (tBTA_JV *)&evt_data, bta_jv_cb.l2c_cb[handle].user_data);
+ }
+
+ return(status);
+}
+/*******************************************************************************
+**
+** Function BTA_JvL2capReady
+**
+** Description This function determined if there is data to read from
+** an L2CAP connection
+**
+** Returns BTA_JV_SUCCESS, if data queue size is in *p_data_size.
+** BTA_JV_FAILURE, if error.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capReady(UINT32 handle, UINT32 *p_data_size)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+
+ APPL_TRACE_API( "%s: %d", __func__, handle);
+ if (p_data_size && handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback)
+ {
+ *p_data_size = 0;
+ if(BT_PASS == GAP_GetRxQueueCnt((UINT16)handle, p_data_size) )
+ {
+ status = BTA_JV_SUCCESS;
+ }
+ }
+
+ return(status);
+}
+
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capWrite
+**
+** Description This function writes data to an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_WRITE_EVT. Works for
+** PSM-based connections
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capWrite(UINT32 handle, UINT32 req_id, UINT8 *p_data,
+ UINT16 len, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_WRITE *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if (handle < BTA_JV_MAX_L2C_CONN && bta_jv_cb.l2c_cb[handle].p_cback &&
+ (p_msg = (tBTA_JV_API_L2CAP_WRITE *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_WRITE))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_WRITE_EVT;
+ p_msg->handle = handle;
+ p_msg->req_id = req_id;
+ p_msg->p_data = p_data;
+ p_msg->p_cb = &bta_jv_cb.l2c_cb[handle];
+ p_msg->len = len;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_JvL2capWriteFixed
+**
+** Description This function writes data to an L2CAP connection
+** When the operation is complete, tBTA_JV_L2CAP_CBACK is
+** called with BTA_JV_L2CAP_WRITE_EVT. Works for
+** fixed-channel connections
+**
+** Returns BTA_JV_SUCCESS, if the request is being processed.
+** BTA_JV_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_JV_STATUS BTA_JvL2capWriteFixed(UINT16 channel, BD_ADDR *addr, UINT32 req_id,
+ tBTA_JV_L2CAP_CBACK *p_cback, UINT8 *p_data, UINT16 len, void *user_data)
+{
+ tBTA_JV_STATUS status = BTA_JV_FAILURE;
+ tBTA_JV_API_L2CAP_WRITE_FIXED *p_msg;
+
+ APPL_TRACE_API( "%s", __func__);
+
+ if ((p_msg =
+ (tBTA_JV_API_L2CAP_WRITE_FIXED *)GKI_getbuf(sizeof(tBTA_JV_API_L2CAP_WRITE_FIXED))) != NULL)
+ {
+ p_msg->hdr.event = BTA_JV_API_L2CAP_WRITE_FIXED_EVT;
+ p_msg->channel = channel;
+ memcpy(p_msg->addr, addr, sizeof(p_msg->addr));
+ p_msg->req_id = req_id;
+ p_msg->p_data = p_data;
+ p_msg->p_cback = p_cback;
+ p_msg->len = len;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ status = BTA_JV_SUCCESS;
+ }
+
+ return(status);
+}
+
+/*******************************************************************************
+**
** Function BTA_JvRfcommConnect
**
** Description This function makes an RFCOMM conection to a remote BD
diff --git a/bta/jv/bta_jv_int.h b/bta/jv/bta_jv_int.h
index a76664704..eaaa75ca0 100644
--- a/bta/jv/bta_jv_int.h
+++ b/bta/jv/bta_jv_int.h
@@ -39,9 +39,17 @@ enum
/* these events are handled by the state machine */
BTA_JV_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_JV),
BTA_JV_API_DISABLE_EVT,
+ BTA_JV_API_GET_CHANNEL_EVT,
+ BTA_JV_API_FREE_SCN_EVT,
BTA_JV_API_START_DISCOVERY_EVT,
BTA_JV_API_CREATE_RECORD_EVT,
BTA_JV_API_DELETE_RECORD_EVT,
+ BTA_JV_API_L2CAP_CONNECT_EVT,
+ BTA_JV_API_L2CAP_CLOSE_EVT,
+ BTA_JV_API_L2CAP_START_SERVER_EVT,
+ BTA_JV_API_L2CAP_STOP_SERVER_EVT,
+ BTA_JV_API_L2CAP_READ_EVT,
+ BTA_JV_API_L2CAP_WRITE_EVT,
BTA_JV_API_RFCOMM_CONNECT_EVT,
BTA_JV_API_RFCOMM_CLOSE_EVT,
BTA_JV_API_RFCOMM_START_SERVER_EVT,
@@ -50,6 +58,11 @@ enum
BTA_JV_API_RFCOMM_WRITE_EVT,
BTA_JV_API_SET_PM_PROFILE_EVT,
BTA_JV_API_PM_STATE_CHANGE_EVT,
+ BTA_JV_API_L2CAP_CONNECT_LE_EVT,
+ BTA_JV_API_L2CAP_START_SERVER_LE_EVT,
+ BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT,
+ BTA_JV_API_L2CAP_WRITE_FIXED_EVT,
+ BTA_JV_API_L2CAP_CLOSE_FIXED_EVT,
BTA_JV_MAX_INT_EVT
};
@@ -104,6 +117,18 @@ enum
} ;
typedef UINT8 tBTA_JV_STATE;
#define BTA_JV_ST_CL_MAX BTA_JV_ST_CL_CLOSING
+/* JV L2CAP control block */
+typedef struct
+{
+ tBTA_JV_L2CAP_CBACK *p_cback; /* the callback function */
+ UINT16 psm; /* the psm used for this server connection */
+ tBTA_JV_STATE state; /* the state of this control block */
+ tBTA_SERVICE_ID sec_id; /* service id */
+ UINT32 handle; /* the handle reported to java app (same as gap handle) */
+ BOOLEAN cong; /* TRUE, if congested */
+ tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */
+ void *user_data; /* user data for callback from higher layers */
+} tBTA_JV_L2C_CB;
#define BTA_JV_RFC_HDL_MASK 0xFF
#define BTA_JV_RFCOMM_MASK 0x80
@@ -115,12 +140,12 @@ typedef UINT8 tBTA_JV_STATE;
typedef struct
{
UINT32 handle; /* the rfcomm session handle at jv */
- UINT16 port_handle; /* port handle */
+ UINT16 port_handle;/* port handle */
tBTA_JV_STATE state; /* the state of this control block */
UINT8 max_sess; /* max sessions */
void *user_data; /* piggyback caller's private data*/
BOOLEAN cong; /* TRUE, if congested */
- tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */
+ tBTA_JV_PM_CB *p_pm_cb; /* ptr to pm control block, NULL: unused */
} tBTA_JV_PCB;
/* JV RFCOMM control block */
@@ -135,6 +160,90 @@ typedef struct
int curr_sess; /* current sessions count*/
} tBTA_JV_RFC_CB;
+/* data type for BTA_JV_API_L2CAP_CONNECT_EVT & BTA_JV_API_L2CAP_CONNECT_LE_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ tBTA_SEC sec_mask;
+ tBTA_JV_ROLE role;
+ union {
+ UINT16 remote_psm;
+ UINT16 remote_chan;
+ };
+ UINT16 rx_mtu;
+ BD_ADDR peer_bd_addr;
+ INT32 has_cfg;
+ tL2CAP_CFG_INFO cfg;
+ INT32 has_ertm_info;
+ tL2CAP_ERTM_INFO ertm_info;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ void *user_data;
+} tBTA_JV_API_L2CAP_CONNECT;
+
+/* data type for BTA_JV_API_L2CAP_SERVER_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ tBTA_SEC sec_mask;
+ tBTA_JV_ROLE role;
+ union {
+ UINT16 local_psm;
+ UINT16 local_chan;
+ };
+ UINT16 rx_mtu;
+ INT32 has_cfg;
+ tL2CAP_CFG_INFO cfg;
+ INT32 has_ertm_info;
+ tL2CAP_ERTM_INFO ertm_info;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ void *user_data;
+} tBTA_JV_API_L2CAP_SERVER;
+
+/* data type for BTA_JV_API_L2CAP_CLOSE_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT32 handle;
+ tBTA_JV_L2C_CB *p_cb;
+} tBTA_JV_API_L2CAP_CLOSE;
+
+/* data type for BTA_JV_API_L2CAP_READ_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT32 handle;
+ UINT32 req_id;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ UINT8* p_data;
+ UINT16 len;
+ void *user_data;
+} tBTA_JV_API_L2CAP_READ;
+
+/* data type for BTA_JV_API_L2CAP_WRITE_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT32 handle;
+ UINT32 req_id;
+ tBTA_JV_L2C_CB *p_cb;
+ UINT8 *p_data;
+ UINT16 len;
+ void *user_data;
+} tBTA_JV_API_L2CAP_WRITE;
+
+/* data type for BTA_JV_API_L2CAP_WRITE_FIXED_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT16 channel;
+ BD_ADDR addr;
+ UINT32 req_id;
+ tBTA_JV_L2CAP_CBACK *p_cback;
+ UINT8 *p_data;
+ UINT16 len;
+ void *user_data;
+} tBTA_JV_API_L2CAP_WRITE_FIXED;
+
/* data type for BTA_JV_API_RFCOMM_CONNECT_EVT */
typedef struct
{
@@ -196,7 +305,7 @@ typedef struct
UINT32 handle;
UINT32 req_id;
UINT8 *p_data;
- int len;
+ int len;
tBTA_JV_RFC_CB *p_cb;
tBTA_JV_PCB *p_pcb;
} tBTA_JV_API_RFCOMM_WRITE;
@@ -208,7 +317,7 @@ typedef struct
UINT32 handle;
tBTA_JV_RFC_CB *p_cb;
tBTA_JV_PCB *p_pcb;
- void *user_data;
+ void *user_data;
} tBTA_JV_API_RFCOMM_CLOSE;
/* data type for BTA_JV_API_CREATE_RECORD_EVT */
@@ -228,6 +337,22 @@ typedef struct
INT32 value_size;
} tBTA_JV_API_ADD_ATTRIBUTE;
+/* data type for BTA_JV_API_FREE_SCN_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ INT32 type; /* One of BTA_JV_CONN_TYPE_ */
+ UINT16 scn;
+} tBTA_JV_API_FREE_CHANNEL;
+
+/* data type for BTA_JV_API_ALLOC_CHANNEL_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ INT32 type; /* One of BTA_JV_CONN_TYPE_ */
+ INT32 channel; /* optionally request a specific channel */
+ void *user_data;
+} tBTA_JV_API_ALLOC_CHANNEL;
/* union of all data types */
typedef union
{
@@ -235,8 +360,15 @@ typedef union
BT_HDR hdr;
tBTA_JV_API_ENABLE enable;
tBTA_JV_API_START_DISCOVERY start_discovery;
+ tBTA_JV_API_ALLOC_CHANNEL alloc_channel;
+ tBTA_JV_API_FREE_CHANNEL free_channel;
tBTA_JV_API_CREATE_RECORD create_record;
tBTA_JV_API_ADD_ATTRIBUTE add_attr;
+ tBTA_JV_API_L2CAP_CONNECT l2cap_connect;
+ tBTA_JV_API_L2CAP_READ l2cap_read;
+ tBTA_JV_API_L2CAP_WRITE l2cap_write;
+ tBTA_JV_API_L2CAP_CLOSE l2cap_close;
+ tBTA_JV_API_L2CAP_SERVER l2cap_server;
tBTA_JV_API_RFCOMM_CONNECT rfcomm_connect;
tBTA_JV_API_RFCOMM_READ rfcomm_read;
tBTA_JV_API_RFCOMM_WRITE rfcomm_write;
@@ -244,6 +376,7 @@ typedef union
tBTA_JV_API_PM_STATE_CHANGE change_pm_state;
tBTA_JV_API_RFCOMM_CLOSE rfcomm_close;
tBTA_JV_API_RFCOMM_SERVER rfcomm_server;
+ tBTA_JV_API_L2CAP_WRITE_FIXED l2cap_write_fixed;
} tBTA_JV_MSG;
/* JV control block */
@@ -255,9 +388,14 @@ typedef struct
UINT32 sdp_handle[BTA_JV_MAX_SDP_REC]; /* SDP records created */
UINT8 *p_sel_raw_data;/* the raw data of last service select */
tBTA_JV_DM_CBACK *p_dm_cback;
+ tBTA_JV_L2C_CB l2c_cb[BTA_JV_MAX_L2C_CONN]; /* index is GAP handle (index) */
tBTA_JV_RFC_CB rfc_cb[BTA_JV_MAX_RFC_CONN];
- tBTA_JV_PCB port_cb[MAX_RFC_PORTS]; /* index of this array is the port_handle, */
+ tBTA_JV_PCB port_cb[MAX_RFC_PORTS]; /* index of this array is
+ the port_handle, */
UINT8 sec_id[BTA_JV_NUM_SERVICE_ID]; /* service ID */
+ BOOLEAN scn[BTA_JV_MAX_SCN]; /* SCN allocated by java */
+ UINT16 free_psm_list[BTA_JV_MAX_L2C_CONN]; /* PSMs freed by java
+ (can be reused) */
UINT8 sdp_active; /* see BTA_JV_SDP_ACT_* */
tSDP_UUID uuid; /* current uuid of sdp discovery*/
tBTA_JV_PM_CB pm_cb[BTA_JV_PM_MAX_NUM]; /* PM on a per JV handle bases */
@@ -285,9 +423,17 @@ extern BOOLEAN bta_jv_sm_execute(BT_HDR *p_msg);
extern void bta_jv_enable (tBTA_JV_MSG *p_data);
extern void bta_jv_disable (tBTA_JV_MSG *p_data);
+extern void bta_jv_get_channel_id (tBTA_JV_MSG *p_data);
+extern void bta_jv_free_scn (tBTA_JV_MSG *p_data);
extern void bta_jv_start_discovery (tBTA_JV_MSG *p_data);
extern void bta_jv_create_record (tBTA_JV_MSG *p_data);
extern void bta_jv_delete_record (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_connect (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_close (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_start_server (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_stop_server (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_read (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_write (tBTA_JV_MSG *p_data);
extern void bta_jv_rfcomm_connect (tBTA_JV_MSG *p_data);
extern void bta_jv_rfcomm_close (tBTA_JV_MSG *p_data);
extern void bta_jv_rfcomm_start_server (tBTA_JV_MSG *p_data);
@@ -296,5 +442,10 @@ extern void bta_jv_rfcomm_read (tBTA_JV_MSG *p_data);
extern void bta_jv_rfcomm_write (tBTA_JV_MSG *p_data);
extern void bta_jv_set_pm_profile (tBTA_JV_MSG *p_data);
extern void bta_jv_change_pm_state(tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_connect_le (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_start_server_le (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_stop_server_le (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_write_fixed (tBTA_JV_MSG *p_data);
+extern void bta_jv_l2cap_close_fixed (tBTA_JV_MSG *p_data);
#endif /* BTA_JV_INT_H */
diff --git a/bta/jv/bta_jv_main.c b/bta/jv/bta_jv_main.c
index 97e2b818e..eb22338f7 100644
--- a/bta/jv/bta_jv_main.c
+++ b/bta/jv/bta_jv_main.c
@@ -46,9 +46,17 @@ const tBTA_JV_ACTION bta_jv_action[] =
{
bta_jv_enable, /* BTA_JV_API_ENABLE_EVT */
bta_jv_disable, /* BTA_JV_API_DISABLE_EVT */
+ bta_jv_get_channel_id, /* BTA_JV_API_GET_CHANNEL_EVT */
+ bta_jv_free_scn, /* BTA_JV_API_FREE_SCN_EVT */
bta_jv_start_discovery, /* BTA_JV_API_START_DISCOVERY_EVT */
bta_jv_create_record, /* BTA_JV_API_CREATE_RECORD_EVT */
bta_jv_delete_record, /* BTA_JV_API_DELETE_RECORD_EVT */
+ bta_jv_l2cap_connect, /* BTA_JV_API_L2CAP_CONNECT_EVT */
+ bta_jv_l2cap_close, /* BTA_JV_API_L2CAP_CLOSE_EVT */
+ bta_jv_l2cap_start_server, /* BTA_JV_API_L2CAP_START_SERVER_EVT */
+ bta_jv_l2cap_stop_server, /* BTA_JV_API_L2CAP_STOP_SERVER_EVT */
+ bta_jv_l2cap_read, /* BTA_JV_API_L2CAP_READ_EVT */
+ bta_jv_l2cap_write, /* BTA_JV_API_L2CAP_WRITE_EVT */
bta_jv_rfcomm_connect, /* BTA_JV_API_RFCOMM_CONNECT_EVT */
bta_jv_rfcomm_close, /* BTA_JV_API_RFCOMM_CLOSE_EVT */
bta_jv_rfcomm_start_server, /* BTA_JV_API_RFCOMM_START_SERVER_EVT */
@@ -57,6 +65,11 @@ const tBTA_JV_ACTION bta_jv_action[] =
bta_jv_rfcomm_write, /* BTA_JV_API_RFCOMM_WRITE_EVT */
bta_jv_set_pm_profile, /* BTA_JV_API_SET_PM_PROFILE_EVT */
bta_jv_change_pm_state, /* BTA_JV_API_PM_STATE_CHANGE_EVT */
+ bta_jv_l2cap_connect_le, /* BTA_JV_API_L2CAP_CONNECT_LE_EVT */
+ bta_jv_l2cap_start_server_le, /* BTA_JV_API_L2CAP_START_SERVER_LE_EVT */
+ bta_jv_l2cap_stop_server_le, /* BTA_JV_API_L2CAP_STOP_SERVER_LE_EVT */
+ bta_jv_l2cap_write_fixed, /* BTA_JV_API_L2CAP_WRITE_FIXED_EVT */
+ bta_jv_l2cap_close_fixed, /* BTA_JV_API_L2CAP_CLOSE_FIXED_EVT */
};
/*******************************************************************************
diff --git a/bta/sdp/bta_sdp.c b/bta/sdp/bta_sdp.c
new file mode 100644
index 000000000..e1a2995cf
--- /dev/null
+++ b/bta/sdp/bta_sdp.c
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+
+ * 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 is the main implementation file for the BTA MCE I/F
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bta_sys.h"
+#include "bta_sdp_api.h"
+#include "bta_sdp_int.h"
+
+/*****************************************************************************
+** Constants and types
+*****************************************************************************/
+
+#if BTA_DYNAMIC_MEMORY == FALSE
+tBTA_SDP_CB bta_sdp_cb;
+#endif
+
+/* state machine action enumeration list */
+#define BTA_SDP_NUM_ACTIONS (BTA_SDP_MAX_INT_EVT & 0x00ff)
+
+/* type for action functions */
+typedef void (*tBTA_SDP_ACTION)(tBTA_SDP_MSG *p_data);
+
+/* action function list */
+const tBTA_SDP_ACTION bta_sdp_action[] =
+{
+ bta_sdp_enable, /* BTA_SDP_API_ENABLE_EVT */
+ bta_sdp_search, /* BTA_SDP_API_SEARCH_EVT */
+ bta_sdp_create_record, /* BTA_SDP_API_CREATE_RECORD_USER_EVT */
+ bta_sdp_remove_record, /* BTA_SDP_API_REMOVE_RECORD_USER_EVT */
+};
+
+/*******************************************************************************
+** Function bta_sdp_sm_execute
+**
+** Description State machine event handling function for SDP search
+**
+** Returns void
+*******************************************************************************/
+BOOLEAN bta_sdp_sm_execute(BT_HDR *p_msg)
+{
+ if(p_msg == NULL) return FALSE;
+
+ BOOLEAN ret = FALSE;
+ UINT16 action = (p_msg->event & 0x00ff);
+
+ /* execute action functions */
+ if(action < BTA_SDP_NUM_ACTIONS)
+ {
+ (*bta_sdp_action[action])((tBTA_SDP_MSG*)p_msg);
+ ret = TRUE;
+ }
+
+ return(ret);
+}
diff --git a/bta/sdp/bta_sdp_act.c b/bta/sdp/bta_sdp_act.c
new file mode 100644
index 000000000..e45042ce2
--- /dev/null
+++ b/bta/sdp/bta_sdp_act.c
@@ -0,0 +1,570 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 action functions for SDP search.
+ ******************************************************************************/
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sdp.h>
+#include <arpa/inet.h>
+
+#include "bt_types.h"
+#include "gki.h"
+#include "utl.h"
+#include "bta_sys.h"
+#include "bta_api.h"
+#include "bta_sdp_api.h"
+#include "bta_sdp_int.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "sdp_api.h"
+#include <string.h>
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+static const uint8_t UUID_MAP_MNS[] = {0x00, 0x00, 0x11, 0x33, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+static const uint8_t UUID_SPP[] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+
+// TODO:
+// Both the fact that the UUIDs are declared in multiple places, plus the fact
+// that there is a mess of UUID comparison and shortening methods will have to
+// be fixed.
+// The btcore->uuid module should be used for all instances.
+
+#define UUID_MAX_LENGTH 16
+#define IS_UUID(u1,u2) !memcmp(u1,u2,UUID_MAX_LENGTH)
+
+static inline tBT_UUID shorten_sdp_uuid(const tBT_UUID* u)
+{
+ static uint8_t bt_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+
+ APPL_TRACE_DEBUG("uuid len:%d", u->len);
+ if(u->len != 16)
+ return *u;
+
+ if(memcmp(&u->uu.uuid128[4], &bt_base_uuid[4], 12) != 0)
+ return *u;
+
+ tBT_UUID su;
+ memset(&su, 0, sizeof(su));
+ if(u->uu.uuid128[0] == 0 && u->uu.uuid128[1] == 0)
+ {
+ su.len = 2;
+ uint16_t u16;
+ memcpy(&u16, &u->uu.uuid128[2], sizeof(u16));
+ su.uu.uuid16 = ntohs(u16);
+ } else {
+ su.len = 4;
+ uint32_t u32;
+ memcpy(&u32, &u->uu.uuid128[0], sizeof(u32));
+ su.uu.uuid32 = ntohl(u32);
+ }
+ return su;
+}
+
+static void bta_create_mns_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec)
+{
+ tSDP_DISCOVERY_DB *db = p_bta_sdp_cfg->p_sdp_db;
+ tSDP_DISC_ATTR *p_attr;
+ tSDP_PROTOCOL_ELEM pe;
+ UINT16 pversion = 0;
+ UINT8 offset = 0;
+ record->mns.hdr.type = SDP_TYPE_MAP_MNS;
+ record->mns.hdr.service_name_length = 0;
+ record->mns.hdr.service_name = NULL;
+ record->mns.hdr.rfcomm_channel_number = 0;
+ record->mns.hdr.l2cap_psm = -1;
+ record->mns.hdr.profile_version = 0;
+ record->mns.supported_features = 0x0000001F; //default value if not found
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES)) != NULL)
+ {
+ record->mns.supported_features = p_attr->attr_value.v.u32;
+ APPL_TRACE_DEBUG("Found supported_features: %d", record->mns.supported_features );
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL)
+ {
+ record->mns.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+ record->mns.hdr.service_name = (char *)p_attr->attr_value.v.array;
+ APPL_TRACE_DEBUG("Found service name with length: %d", record->mns.hdr.service_name_length);
+ }
+
+ if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE, &pversion))
+ {
+ record->mns.hdr.profile_version = pversion;
+ APPL_TRACE_DEBUG("Found profile_version: %d", record->mns.hdr.profile_version);
+ }
+
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ record->mns.hdr.rfcomm_channel_number = pe.params[0];
+ APPL_TRACE_DEBUG("Found rfcomm_channel_number: %d", record->mns.hdr.rfcomm_channel_number);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL)
+ {
+ record->mns.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+ APPL_TRACE_DEBUG("Found l2cap_psm: %d", record->mns.hdr.l2cap_psm);
+ }
+}
+
+static void bta_create_mas_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec)
+{
+ tSDP_DISCOVERY_DB *db = p_bta_sdp_cfg->p_sdp_db;
+ tSDP_DISC_ATTR *p_attr;
+ tSDP_PROTOCOL_ELEM pe;
+ UINT16 pversion = -1;
+
+ record->mas.hdr.type = SDP_TYPE_MAP_MAS;
+ record->mas.hdr.service_name_length = 0;
+ record->mas.hdr.service_name = NULL;
+ record->mas.hdr.rfcomm_channel_number = 0;
+ record->mas.hdr.l2cap_psm = -1;
+ record->mas.hdr.profile_version = 0;
+ record->mas.mas_instance_id = 0;
+ record->mas.supported_features = 0x0000001F;
+ record->mas.supported_message_types = 0;
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAS_INSTANCE_ID)) != NULL)
+ {
+ record->mas.mas_instance_id = p_attr->attr_value.v.u8;
+ APPL_TRACE_DEBUG("Found mas_instance_id: %d", record->mas.mas_instance_id);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_MSG_TYPE)) != NULL)
+ {
+ record->mas.supported_message_types = p_attr->attr_value.v.u8;
+ APPL_TRACE_DEBUG("Found supported_message_types: %d", record->mas.supported_message_types);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES)) != NULL)
+ {
+ record->mas.supported_features = p_attr->attr_value.v.u32;
+ APPL_TRACE_DEBUG("Found supported_features: %d", record->mas.supported_features);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL)
+ {
+ record->mas.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+ record->mas.hdr.service_name = (char *)p_attr->attr_value.v.array;
+ APPL_TRACE_DEBUG("Found service name with length: %d", record->mas.hdr.service_name_length);
+ }
+
+ if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_MAP_PROFILE, &pversion))
+ {
+ record->mas.hdr.profile_version = pversion;
+ APPL_TRACE_DEBUG("Found profile_version: %d", record->mas.hdr.profile_version);
+ }
+
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ record->mas.hdr.rfcomm_channel_number = pe.params[0];
+ APPL_TRACE_DEBUG("Found rfcomm_channel_number: %d", record->mas.hdr.rfcomm_channel_number);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL)
+ {
+ record->mas.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+ APPL_TRACE_DEBUG("Found l2cap_psm: %d", record->mas.hdr.l2cap_psm);
+
+ }
+}
+
+static void bta_create_pse_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec)
+{
+ tSDP_DISCOVERY_DB *db = p_bta_sdp_cfg->p_sdp_db;
+ tSDP_DISC_ATTR *p_attr;
+ UINT16 pversion;
+ tSDP_PROTOCOL_ELEM pe;
+
+ record->pse.hdr.type = SDP_TYPE_PBAP_PSE;
+ record->pse.hdr.service_name_length = 0;
+ record->pse.hdr.service_name = NULL;
+ record->pse.hdr.rfcomm_channel_number = 0;
+ record->pse.hdr.l2cap_psm = -1;
+ record->pse.hdr.profile_version = 0;
+ record->pse.supported_features = 0x00000003;
+ record->pse.supported_repositories = 0;
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_REPOSITORIES)) != NULL)
+ {
+ record->pse.supported_repositories = p_attr->attr_value.v.u8;
+ APPL_TRACE_DEBUG("Found supported_repositories: %d", record->pse.supported_repositories);
+ }
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES)) != NULL)
+ {
+ record->pse.supported_features = p_attr->attr_value.v.u32;
+ APPL_TRACE_DEBUG("Found supported_features: %d", record->pse.supported_features);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL)
+ {
+ record->pse.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+ record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array;
+ APPL_TRACE_DEBUG("Found service name with length: %d", record->pse.hdr.service_name_length);
+ }
+
+ if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &pversion))
+ {
+ record->pse.hdr.profile_version = pversion;
+ APPL_TRACE_DEBUG("Found profile_version: %d", record->pse.hdr.profile_version);
+ }
+
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ record->pse.hdr.rfcomm_channel_number = pe.params[0];
+ APPL_TRACE_DEBUG("Found rfcomm_channel_number: %d", record->pse.hdr.rfcomm_channel_number);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL)
+ {
+ record->pse.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+ APPL_TRACE_DEBUG("Found l2cap_psm: %d", record->pse.hdr.l2cap_psm);
+ }
+
+}
+
+static void bta_create_ops_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec)
+{
+ tSDP_DISCOVERY_DB *db = p_bta_sdp_cfg->p_sdp_db;
+ tSDP_DISC_ATTR *p_attr, *p_sattr;
+ tSDP_PROTOCOL_ELEM pe;
+ UINT16 pversion = -1;
+
+ record->ops.hdr.type = SDP_TYPE_OPP_SERVER;
+ record->ops.hdr.service_name_length = 0;
+ record->ops.hdr.service_name = NULL;
+ record->ops.hdr.rfcomm_channel_number = 0;
+ record->ops.hdr.l2cap_psm = -1;
+ record->ops.hdr.profile_version = 0;
+ record->ops.supported_formats_list_len = 0;
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL)
+ {
+ record->ops.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+ record->ops.hdr.service_name = (char *)p_attr->attr_value.v.array;
+ APPL_TRACE_DEBUG("Found service name with length: %d", record->ops.hdr.service_name_length);
+ }
+
+ if (SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_OBEX_OBJECT_PUSH, &pversion))
+ {
+ record->ops.hdr.profile_version = pversion;
+ APPL_TRACE_DEBUG("Found profile_version: %d", record->ops.hdr.profile_version);
+ }
+
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ record->ops.hdr.rfcomm_channel_number = pe.params[0];
+ APPL_TRACE_DEBUG("Found rfcomm_channel_number: %d", record->ops.hdr.rfcomm_channel_number);
+ }
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL)
+ {
+ record->ops.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+ APPL_TRACE_DEBUG("Found l2cap_psm: %d", record->ops.hdr.l2cap_psm);
+
+ }
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FORMATS_LIST)) != NULL)
+ {
+ /* Safety check - each entry should itself be a sequence */
+ if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) {
+ record->ops.supported_formats_list_len = 0;
+ APPL_TRACE_ERROR("supported_formats_list - wrong attribute length/type:"
+ " 0x%02x - expected 0x06",p_attr->attr_len_type);
+ } else {
+ int count = 0;
+ /* 1 byte for type/length 1 byte for value */
+ record->ops.supported_formats_list_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)/2;
+
+ /* Extract each value into */
+ for (p_sattr = p_attr->attr_value.v.p_sub_attr;
+ p_sattr != NULL; p_sattr = p_sattr->p_next_attr)
+ {
+ if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE)
+ && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 1))
+ {
+ if(count == sizeof(record->ops.supported_formats_list)) {
+ APPL_TRACE_ERROR("supported_formats_list - count overflow - "
+ "too many sub attributes!!");
+ /* If you hit this, new formats have been added,
+ * update SDP_OPP_SUPPORTED_FORMATS_MAX_LENGTH */
+ break;
+ }
+ record->ops.supported_formats_list[count] = p_sattr->attr_value.v.u8;
+ count++;
+ } else {
+ APPL_TRACE_ERROR("supported_formats_list - wrong sub attribute "
+ "length/type: 0x%02x - expected 0x80"
+ ,p_sattr->attr_len_type);
+ break;
+ }
+ }
+ if(record->ops.supported_formats_list_len != count) {
+ APPL_TRACE_WARNING("supported_formats_list - Length of attribute different "
+ "from the actual number of sub-attributes in the sequence "
+ "att-length: %d - number of elements: %d"
+ ,record->ops.supported_formats_list_len , count);
+
+ }
+ record->ops.supported_formats_list_len = count;
+ }
+
+ APPL_TRACE_DEBUG("Found supported_formats_list - length: %d",
+ record->ops.supported_formats_list_len);
+ }
+}
+
+static void bta_create_raw_sdp_record(bluetooth_sdp_record *record, tSDP_DISC_REC *p_rec)
+{
+ tSDP_DISCOVERY_DB *db = p_bta_sdp_cfg->p_sdp_db;
+ tSDP_DISC_ATTR *p_attr;
+ UINT16 pversion;
+ tSDP_PROTOCOL_ELEM pe;
+
+ record->hdr.type = SDP_TYPE_RAW;
+ record->hdr.service_name_length = 0;
+ record->hdr.service_name = NULL;
+ record->hdr.rfcomm_channel_number = -1;
+ record->hdr.l2cap_psm = -1;
+ record->hdr.profile_version = -1;
+
+ /* Try to extract a service name */
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SERVICE_NAME)) != NULL)
+ {
+ record->pse.hdr.service_name_length = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+ record->pse.hdr.service_name = (char *)p_attr->attr_value.v.array;
+ APPL_TRACE_DEBUG("Found service name with length: %d", record->pse.hdr.service_name_length);
+ }
+
+ /* Try to extract an RFCOMM channel */
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ record->pse.hdr.rfcomm_channel_number = pe.params[0];
+ APPL_TRACE_DEBUG("Found rfcomm_channel_number: %d", record->pse.hdr.rfcomm_channel_number);
+ }
+ record->hdr.user1_ptr_len = p_bta_sdp_cfg->p_sdp_db->raw_size;
+ record->hdr.user1_ptr = p_bta_sdp_cfg->p_sdp_db->raw_data;
+}
+
+
+/*******************************************************************************
+**
+** Function bta_sdp_search_cback
+**
+** Description Callback from btm after search is completed
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_sdp_search_cback(UINT16 result, void * user_data)
+{
+ tSDP_DISC_REC *p_rec = NULL;
+ tBTA_SDP_SEARCH_COMP evt_data = {0}; // We need to zero-initialize
+ tBTA_SDP_STATUS status = BTA_SDP_FAILURE;
+ UINT16 uuid16 = 0;
+ int count = 0;
+ tBT_UUID su;
+ APPL_TRACE_DEBUG("bta_sdp_search_cback res: 0x%x", result);
+
+ bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
+
+ if (bta_sdp_cb.p_dm_cback == NULL) return;
+
+ bdcpy(evt_data.remote_addr, bta_sdp_cb.remote_addr);
+ tBT_UUID *uuid = (tBT_UUID*)user_data;
+ memcpy(&evt_data.uuid, uuid, sizeof(tBT_UUID));
+ su = shorten_sdp_uuid(uuid);
+
+ if (result == SDP_SUCCESS || result == SDP_DB_FULL)
+ {
+ do {
+ p_rec = SDP_FindServiceUUIDInDb(p_bta_sdp_cfg->p_sdp_db, &su, p_rec);
+ /* generate the matching record data pointer */
+ if(p_rec != NULL){
+ status = BTA_SDP_SUCCESS;
+ if (IS_UUID(UUID_MAP_MAS,uuid->uu.uuid128)) {
+ APPL_TRACE_DEBUG("%s() - found MAP (MAS) uuid", __func__);
+ bta_create_mas_sdp_record(&evt_data.records[count], p_rec);
+
+ }else if (IS_UUID(UUID_MAP_MNS,uuid->uu.uuid128)) {
+ APPL_TRACE_DEBUG("%s() - found MAP (MNS) uuid", __func__);
+ bta_create_mns_sdp_record(&evt_data.records[count], p_rec);
+
+ }else if(IS_UUID(UUID_PBAP_PSE,uuid->uu.uuid128)){
+ APPL_TRACE_DEBUG("%s() - found PBAP (PSE) uuid", __func__);
+ bta_create_pse_sdp_record(&evt_data.records[count], p_rec);
+ }else if(IS_UUID(UUID_OBEX_OBJECT_PUSH,uuid->uu.uuid128)){
+ APPL_TRACE_DEBUG("%s() - found Object Push Server (OPS) uuid", __func__);
+ bta_create_ops_sdp_record(&evt_data.records[count], p_rec);
+ }else {
+
+ /* we do not have specific structure for this */
+ APPL_TRACE_DEBUG("%s() - profile not identified. using raw data", __func__);
+ bta_create_raw_sdp_record(&evt_data.records[count], p_rec);
+ p_rec = NULL; // Terminate loop
+ /* For raw, we only extract the first entry, and then return the entire
+ raw data chunk.
+ TODO: Find a way to split the raw data into record chunks, and iterate
+ to extract generic data for each chunk - e.g. rfcomm channel and
+ service name. */
+ }
+ count++;
+ } else {
+ APPL_TRACE_DEBUG("%s() - UUID not found", __func__);
+ }
+ } while (p_rec != NULL && count < BTA_SDP_MAX_RECORDS);
+
+ evt_data.record_count = count;
+ }
+ evt_data.status = status;
+
+ bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP*) &evt_data, (void*)&uuid->uu.uuid128);
+ free(user_data); // We no longer need the user data to track the search
+}
+
+/*******************************************************************************
+**
+** Function bta_sdp_enable
+**
+** Description Initializes the SDP I/F
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_sdp_enable(tBTA_SDP_MSG *p_data)
+{
+ APPL_TRACE_DEBUG("%s in, sdp_active:%d", __func__, bta_sdp_cb.sdp_active);
+ tBTA_SDP_STATUS status = BTA_SDP_SUCCESS;
+ bta_sdp_cb.p_dm_cback = p_data->enable.p_cback;
+ bta_sdp_cb.p_dm_cback(BTA_SDP_ENABLE_EVT, (tBTA_SDP *)&status, NULL);
+}
+
+/*******************************************************************************
+**
+** Function bta_sdp_search
+**
+** Description Discovers all sdp records for an uuid on remote device
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_sdp_search(tBTA_SDP_MSG *p_data)
+{
+ int x=0;
+ // TODO: Leaks!!! but needed as user-data pointer
+ tBT_UUID *bta_sdp_search_uuid = malloc(sizeof(tBT_UUID));
+ if(p_data == NULL)
+ {
+ APPL_TRACE_DEBUG("SDP control block handle is null");
+ return;
+ }
+ tBTA_SDP_STATUS status = BTA_SDP_FAILURE;
+
+ APPL_TRACE_DEBUG("%s in, sdp_active:%d", __func__, bta_sdp_cb.sdp_active);
+
+ if (bta_sdp_cb.sdp_active != BTA_SDP_ACTIVE_NONE)
+ {
+ /* SDP is still in progress */
+ status = BTA_SDP_BUSY;
+ if(bta_sdp_cb.p_dm_cback) {
+ tBTA_SDP_SEARCH_COMP result = {0};
+ result.uuid = p_data->get_search.uuid;
+ bdcpy(result.remote_addr, p_data->get_search.bd_addr);
+ result.status = status;
+ bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP *)&result, NULL);
+ }
+ return;
+ }
+
+ bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_YES;
+ bdcpy(bta_sdp_cb.remote_addr, p_data->get_search.bd_addr);
+ /* set the uuid used in the search */
+ memcpy(bta_sdp_search_uuid, &(p_data->get_search.uuid),sizeof(tBT_UUID));
+
+ /* initialize the search for the uuid */
+ APPL_TRACE_DEBUG("%s init discovery with UUID(len: %d):",
+ __func__, bta_sdp_search_uuid->len);
+ for(x = 0; x<bta_sdp_search_uuid->len;x++){
+ APPL_TRACE_DEBUG("%X",bta_sdp_search_uuid->uu.uuid128[x]);
+ }
+ SDP_InitDiscoveryDb (p_bta_sdp_cfg->p_sdp_db, p_bta_sdp_cfg->sdp_db_size, 1,
+ bta_sdp_search_uuid, 0, NULL);
+
+ if (!SDP_ServiceSearchAttributeRequest2(p_data->get_search.bd_addr, p_bta_sdp_cfg->p_sdp_db,
+ bta_sdp_search_cback, (void*)bta_sdp_search_uuid))
+ {
+ bta_sdp_cb.sdp_active = BTA_SDP_ACTIVE_NONE;
+
+ /* failed to start SDP. report the failure right away */
+ if (bta_sdp_cb.p_dm_cback) {
+ tBTA_SDP_SEARCH_COMP result = {0};
+ result.uuid = p_data->get_search.uuid;
+ bdcpy(result.remote_addr, p_data->get_search.bd_addr);
+ result.status = status;
+ bta_sdp_cb.p_dm_cback(BTA_SDP_SEARCH_COMP_EVT, (tBTA_SDP *)&result, NULL);
+ }
+ }
+ /*
+ else report the result when the cback is called
+ */
+}
+
+/*******************************************************************************
+**
+** Function bta_sdp_record
+**
+** Description Discovers all sdp records for an uuid on remote device
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_sdp_create_record(tBTA_SDP_MSG *p_data)
+{
+ APPL_TRACE_DEBUG("%s() event: %d", __func__, p_data->record.hdr.event);
+ if (bta_sdp_cb.p_dm_cback)
+ bta_sdp_cb.p_dm_cback(BTA_SDP_CREATE_RECORD_USER_EVT, NULL, p_data->record.user_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_sdp_create_record
+**
+** Description Discovers all sdp records for an uuid on remote device
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_sdp_remove_record(tBTA_SDP_MSG *p_data)
+{
+ APPL_TRACE_DEBUG("%s() event: %d", __func__, p_data->record.hdr.event);
+ if (bta_sdp_cb.p_dm_cback)
+ bta_sdp_cb.p_dm_cback(BTA_SDP_REMOVE_RECORD_USER_EVT, NULL, p_data->record.user_data);
+}
diff --git a/bta/sdp/bta_sdp_api.c b/bta/sdp/bta_sdp_api.c
new file mode 100644
index 000000000..d98ddca38
--- /dev/null
+++ b/bta/sdp/bta_sdp_api.c
@@ -0,0 +1,173 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 is the implementation of the API for SDP search subsystem
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bta_sys.h"
+#include "bta_sdp_api.h"
+#include "bta_sdp_int.h"
+#include "gki.h"
+#include <string.h>
+#include "port_api.h"
+#include "sdp_api.h"
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+static const tBTA_SYS_REG bta_sdp_reg =
+{
+ bta_sdp_sm_execute,
+ NULL
+};
+
+/*******************************************************************************
+**
+** Function BTA_SdpEnable
+**
+** Description Enable the SDP search I/F service. When the enable
+** operation is complete the callback function will be
+** called with a BTA_SDP_ENABLE_EVT. This function must
+** be called before other functions in the SDP search API are
+** called.
+**
+** Returns BTA_SDP_SUCCESS if successful.
+** BTA_SDP_FAIL if internal failure.
+**
+*******************************************************************************/
+tBTA_SDP_STATUS BTA_SdpEnable(tBTA_SDP_DM_CBACK *p_cback)
+{
+ tBTA_SDP_STATUS status = BTA_SDP_FAILURE;
+ tBTA_SDP_API_ENABLE *p_buf;
+
+ APPL_TRACE_API(__FUNCTION__);
+ if(p_cback && FALSE == bta_sys_is_register(BTA_ID_SDP))
+ {
+ memset(&bta_sdp_cb, 0, sizeof(tBTA_SDP_CB));
+
+ /* register with BTA system manager */
+ bta_sys_register(BTA_ID_SDP, &bta_sdp_reg);
+
+ if (p_cback &&
+ (p_buf = (tBTA_SDP_API_ENABLE *) GKI_getbuf(sizeof(tBTA_SDP_API_ENABLE))) != NULL)
+ {
+ p_buf->hdr.event = BTA_SDP_API_ENABLE_EVT;
+ p_buf->p_cback = p_cback;
+ bta_sys_sendmsg(p_buf);
+ status = BTA_SDP_SUCCESS;
+ }
+ }
+ return(status);
+}
+
+/*******************************************************************************
+**
+** Function BTA_SdpSearch
+**
+** Description This function performs service discovery for a specific service
+** on given peer device. When the operation is completed
+** the tBTA_SDP_DM_CBACK callback function will be called with
+** a BTA_SDP_SEARCH_COMPLETE_EVT.
+**
+** Returns BTA_SDP_SUCCESS, if the request is being processed.
+** BTA_SDP_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_SDP_STATUS BTA_SdpSearch(BD_ADDR bd_addr, tSDP_UUID *uuid)
+{
+ tBTA_SDP_STATUS ret = BTA_SDP_FAILURE;
+ tBTA_SDP_API_SEARCH *p_msg;
+
+ APPL_TRACE_API(__FUNCTION__);
+ if ((p_msg = (tBTA_SDP_API_SEARCH *)GKI_getbuf(sizeof(tBTA_SDP_API_SEARCH))) != NULL)
+ {
+ p_msg->hdr.event = BTA_SDP_API_SEARCH_EVT;
+ bdcpy(p_msg->bd_addr, bd_addr);
+ //p_msg->uuid = uuid;
+ memcpy(&(p_msg->uuid), uuid, sizeof(tSDP_UUID));
+ bta_sys_sendmsg(p_msg);
+ ret = BTA_SDP_SUCCESS;
+ }
+
+ return(ret);
+}
+
+/*******************************************************************************
+**
+** Function BTA_SdpCreateRecordByUser
+**
+** Description This function is used to request a callback to create a SDP
+** record. The registered callback will be called with event
+** BTA_SDP_CREATE_RECORD_USER_EVT.
+**
+** Returns BTA_SDP_SUCCESS, if the request is being processed.
+** BTA_SDP_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_SDP_STATUS BTA_SdpCreateRecordByUser(void* user_data)
+{
+ tBTA_SDP_STATUS ret = BTA_SDP_FAILURE;
+ tBTA_SDP_API_RECORD_USER *p_msg;
+
+ APPL_TRACE_API(__FUNCTION__);
+ if ((p_msg = (tBTA_SDP_API_RECORD_USER *)GKI_getbuf(sizeof(tBTA_SDP_API_RECORD_USER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_SDP_API_CREATE_RECORD_USER_EVT;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ ret = BTA_SDP_SUCCESS;
+ }
+
+ return(ret);
+}
+
+/*******************************************************************************
+**
+** Function BTA_SdpRemoveRecordByUser
+**
+** Description This function is used to request a callback to remove a SDP
+** record. The registered callback will be called with event
+** BTA_SDP_REMOVE_RECORD_USER_EVT.
+**
+** Returns BTA_SDP_SUCCESS, if the request is being processed.
+** BTA_SDP_FAILURE, otherwise.
+**
+*******************************************************************************/
+tBTA_SDP_STATUS BTA_SdpRemoveRecordByUser(void* user_data)
+{
+ tBTA_SDP_STATUS ret = BTA_SDP_FAILURE;
+ tBTA_SDP_API_RECORD_USER *p_msg;
+
+ APPL_TRACE_API(__FUNCTION__);
+ if ((p_msg = (tBTA_SDP_API_RECORD_USER *)GKI_getbuf(sizeof(tBTA_SDP_API_RECORD_USER))) != NULL)
+ {
+ p_msg->hdr.event = BTA_SDP_API_REMOVE_RECORD_USER_EVT;
+ p_msg->user_data = user_data;
+ bta_sys_sendmsg(p_msg);
+ ret = BTA_SDP_SUCCESS;
+ }
+
+ return(ret);
+}
+
+
diff --git a/bta/sdp/bta_sdp_cfg.c b/bta/sdp/bta_sdp_cfg.c
new file mode 100644
index 000000000..eef2511a5
--- /dev/null
+++ b/bta/sdp/bta_sdp_cfg.c
@@ -0,0 +1,40 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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 compile-time configurable constants for SDP Search
+ ******************************************************************************/
+
+#include "gki.h"
+#include "bta_api.h"
+#include "bta_sdp_api.h"
+
+#ifndef BTA_SDP_DB_SIZE
+#define BTA_SDP_DB_SIZE 4500
+#endif
+
+static UINT8 __attribute__ ((aligned(4))) bta_sdp_db_data[BTA_SDP_DB_SIZE];
+
+/* SDP configuration structure */
+const tBTA_SDP_CFG bta_sdp_cfg =
+{
+ BTA_SDP_DB_SIZE,
+ (tSDP_DISCOVERY_DB *)bta_sdp_db_data /* The data buffer to keep SDP database */
+};
+
+tBTA_SDP_CFG *p_bta_sdp_cfg = (tBTA_SDP_CFG *) &bta_sdp_cfg;
diff --git a/bta/sdp/bta_sdp_int.h b/bta/sdp/bta_sdp_int.h
new file mode 100644
index 000000000..4f3f6a53d
--- /dev/null
+++ b/bta/sdp/bta_sdp_int.h
@@ -0,0 +1,115 @@
+
+
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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 is the private interface file for the BTA SDP I/F
+ *
+ ******************************************************************************/
+#ifndef BTA_SDP_INT_H
+#define BTA_SDP_INT_H
+
+#include "bta_sys.h"
+#include "bta_api.h"
+#include "bta_sdp_api.h"
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+enum
+{
+ /* these events are handled by the state machine */
+ BTA_SDP_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_SDP),
+ BTA_SDP_API_SEARCH_EVT,
+ BTA_SDP_API_CREATE_RECORD_USER_EVT,
+ BTA_SDP_API_REMOVE_RECORD_USER_EVT,
+ BTA_SDP_MAX_INT_EVT
+};
+
+enum
+{
+ BTA_SDP_ACTIVE_NONE = 0,
+ BTA_SDP_ACTIVE_YES /* waiting for SDP result */
+};
+
+
+
+/* data type for BTA_SDP_API_ENABLE_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ tBTA_SDP_DM_CBACK *p_cback;
+} tBTA_SDP_API_ENABLE;
+
+/* data type for BTA_SDP_API_SEARCH_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ BD_ADDR bd_addr;
+ tSDP_UUID uuid;
+} tBTA_SDP_API_SEARCH;
+
+/* data type for BTA_SDP_API_SEARCH_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ void* user_data;
+} tBTA_SDP_API_RECORD_USER;
+
+/* union of all data types */
+typedef union
+{
+ /* GKI event buffer header */
+ BT_HDR hdr;
+ tBTA_SDP_API_ENABLE enable;
+ tBTA_SDP_API_SEARCH get_search;
+ tBTA_SDP_API_RECORD_USER record;
+} tBTA_SDP_MSG;
+
+/* SDP control block */
+typedef struct
+{
+ UINT8 sdp_active; /* see BTA_SDP_SDP_ACT_* */
+ BD_ADDR remote_addr;
+ tBTA_SDP_DM_CBACK *p_dm_cback;
+} tBTA_SDP_CB;
+
+
+/* SDP control block */
+#if BTA_DYNAMIC_MEMORY == FALSE
+extern tBTA_SDP_CB bta_sdp_cb;
+#else
+extern tBTA_SDP_CB *bta_sdp_cb_ptr;
+#define bta_sdp_cb (*bta_sdp_cb_ptr)
+#endif
+
+/* config struct */
+extern tBTA_SDP_CFG *p_bta_sdp_cfg;
+
+extern BOOLEAN bta_sdp_sm_execute(BT_HDR *p_msg);
+
+extern void bta_sdp_enable (tBTA_SDP_MSG *p_data);
+extern void bta_sdp_search (tBTA_SDP_MSG *p_data);
+extern void bta_sdp_create_record(tBTA_SDP_MSG *p_data);
+extern void bta_sdp_remove_record(tBTA_SDP_MSG *p_data);
+
+#endif /* BTA_SDP_INT_H */
diff --git a/bta/sys/bta_sys.h b/bta/sys/bta_sys.h
index f42ee0ae5..4146f9830 100644
--- a/bta/sys/bta_sys.h
+++ b/bta/sys/bta_sys.h
@@ -91,7 +91,8 @@ typedef UINT16 tBTA_SYS_HW_MODULE;
#define BTA_ID_HL 30 /* Health Device Profile*/
#define BTA_ID_GATTC 31 /* GATT Client */
#define BTA_ID_GATTS 32 /* GATT Client */
-#define BTA_ID_BLUETOOTH_MAX 33 /* last BT profile */
+#define BTA_ID_SDP 33 /* SDP Client */
+#define BTA_ID_BLUETOOTH_MAX 34 /* last BT profile */
/* GENERIC */
#define BTA_ID_PRM 38
@@ -102,10 +103,10 @@ typedef UINT16 tBTA_SYS_HW_MODULE;
/* JV */
-#define BTA_ID_JV1 43 /* JV1 */
-#define BTA_ID_JV2 44 /* JV2 */
+#define BTA_ID_JV1 44 /* JV1 */
+#define BTA_ID_JV2 45 /* JV2 */
-#define BTA_ID_MAX (43 + BTA_DM_NUM_JV_ID)
+#define BTA_ID_MAX (44 + BTA_DM_NUM_JV_ID)
typedef UINT8 tBTA_SYS_ID;
diff --git a/btif/include/btif_sdp.h b/btif/include/btif_sdp.h
new file mode 100644
index 000000000..2aac1c31d
--- /dev/null
+++ b/btif/include/btif_sdp.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * Filename: btif_sdp.h
+ *
+ * Description: Bluetooth SDP search Interface
+ *
+ *******************************************************************************/
+
+#ifndef BTIF_SDP_H
+#define BTIF_SDP_H
+
+#include <hardware/bt_sdp.h>
+
+btsdp_interface_t *btif_sdp_get_interface();
+
+#endif
diff --git a/btif/include/btif_sock_l2cap.h b/btif/include/btif_sock_l2cap.h
new file mode 100644
index 000000000..9425dc90c
--- /dev/null
+++ b/btif/include/btif_sock_l2cap.h
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * L2CAP Socket Interface
+ *******************************************************************************/
+
+#ifndef BTIF_SOCK_L2CAP_H
+#define BTIF_SOCK_L2CAP_H
+
+
+
+#define L2CAP_MASK_FIXED_CHANNEL 0x10000
+
+
+bt_status_t btsock_l2cap_init(int handle);
+bt_status_t btsock_l2cap_cleanup();
+bt_status_t btsock_l2cap_listen(const char* name, int channel,
+ int* sock_fd, int flags);
+bt_status_t btsock_l2cap_connect(const bt_bdaddr_t *bd_addr,
+ int channel, int* sock_fd, int flags);
+void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id);
+void on_l2cap_psm_assigned(int id, int psm);
+
+#endif
+
diff --git a/btif/include/btif_sock_sdp.h b/btif/include/btif_sock_sdp.h
index 857c2fba2..3cbc70c9d 100644
--- a/btif/include/btif_sock_sdp.h
+++ b/btif/include/btif_sock_sdp.h
@@ -25,14 +25,14 @@ static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {0x00, 0x00, 0x11, 0x05, 0x00, 0
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_PBAP_PSE[] = {0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
-static const uint8_t UUID_MAPS_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
+static const uint8_t UUID_MAP_MAS[] = {0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t UUID_SPP[] = {0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static inline bool is_uuid_empty(const uint8_t* uuid)
{
- static uint8_t empty_uuid[16];
+ static uint8_t empty_uuid[16];
return uuid == NULL || memcmp(uuid, empty_uuid, sizeof(empty_uuid)) == 0;
}
diff --git a/btif/src/bluetooth.c b/btif/src/bluetooth.c
index 075e9cf86..4558705d4 100644
--- a/btif/src/bluetooth.c
+++ b/btif/src/bluetooth.c
@@ -40,6 +40,7 @@
#include <hardware/bt_mce.h>
#include <hardware/bt_gatt.h>
#include <hardware/bt_rc.h>
+#include <hardware/bt_sdp.h>
#define LOG_NDDEBUG 0
#define LOG_TAG "bt_bluedroid"
@@ -98,6 +99,8 @@ extern btgatt_interface_t *btif_gatt_get_interface();
extern btrc_interface_t *btif_rc_get_interface();
/* avrc controller */
extern btrc_interface_t *btif_rc_ctrl_get_interface();
+/*SDP search client*/
+extern btsdp_interface_t *btif_sdp_get_interface();
/************************************************************************************
** Functions
@@ -337,8 +340,8 @@ static const void* get_profile_interface (const char *profile_id)
if (is_profile(profile_id, BT_PROFILE_HEALTH_ID))
return btif_hl_get_interface();
- if (is_profile(profile_id, BT_PROFILE_MAP_CLIENT_ID))
- return btif_mce_get_interface();
+ if (is_profile(profile_id, BT_PROFILE_SDP_CLIENT_ID))
+ return btif_sdp_get_interface();
#if ( BTA_GATT_INCLUDED == TRUE && BLE_INCLUDED == TRUE)
if (is_profile(profile_id, BT_PROFILE_GATT_ID))
diff --git a/btif/src/btif_core.c b/btif/src/btif_core.c
index 9462bce1e..1049673fd 100644
--- a/btif/src/btif_core.c
+++ b/btif/src/btif_core.c
@@ -47,7 +47,6 @@
#include "btif_av.h"
#include "btif_config.h"
#include "btif_pan.h"
-#include "btif_mce.h"
#include "btif_profile_queue.h"
#include "btif_config.h"
#include "btif_sock.h"
diff --git a/btif/src/btif_dm.c b/btif/src/btif_dm.c
index 073fc7d1b..7a7ec01a3 100644
--- a/btif/src/btif_dm.c
+++ b/btif/src/btif_dm.c
@@ -47,6 +47,7 @@
#include "btif_storage.h"
#include "btif_hh.h"
#include "btif_config.h"
+#include "btif_sdp.h"
#include "bta_gatt_api.h"
#include "include/stack_config.h"
@@ -242,7 +243,7 @@ extern bt_status_t btif_hf_execute_service(BOOLEAN b_enable);
extern bt_status_t btif_av_execute_service(BOOLEAN b_enable);
extern bt_status_t btif_hh_execute_service(BOOLEAN b_enable);
extern bt_status_t btif_hf_client_execute_service(BOOLEAN b_enable);
-extern bt_status_t btif_mce_execute_service(BOOLEAN b_enable);
+extern bt_status_t btif_sdp_execute_service(BOOLEAN b_enable);
extern int btif_hh_connect(bt_bdaddr_t *bd_addr);
extern void bta_gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16);
@@ -254,6 +255,7 @@ extern void bta_gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UIN
bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id,
BOOLEAN b_enable)
{
+ BTIF_TRACE_DEBUG("%s service_id: %d", __FUNCTION__, service_id);
/* Check the service_ID and invoke the profile's BT state changed API */
switch (service_id)
{
@@ -274,9 +276,9 @@ bt_status_t btif_in_execute_service_request(tBTA_SERVICE_ID service_id,
{
btif_hf_client_execute_service(b_enable);
}break;
- case BTA_MAP_SERVICE_ID:
+ case BTA_SDP_SERVICE_ID:
{
- btif_mce_execute_service(b_enable);
+ btif_sdp_execute_service(b_enable);
}break;
default:
BTIF_TRACE_ERROR("%s: Unknown service being enabled", __FUNCTION__);
diff --git a/btif/src/btif_sdp.c b/btif/src/btif_sdp.c
new file mode 100644
index 000000000..c5fd81386
--- /dev/null
+++ b/btif/src/btif_sdp.c
@@ -0,0 +1,197 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Samsung System LSI
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/************************************************************************************
+ *
+ * Filename: btif_sdp.c
+ * Description: SDP Bluetooth Interface.
+ * Implements the generic message handling and search functionality.
+ * References btif_sdp_server.c for SDP record creation.
+ *
+ ***********************************************************************************/
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sdp.h>
+#include <stdlib.h>
+
+#define LOG_TAG "BTIF_SDP"
+#include "btif_common.h"
+#include "btif_util.h"
+#include "btif_profile_queue.h"
+#include "bta_api.h"
+#include "bta_sdp_api.h"
+
+/*****************************************************************************
+** Functions implemented in sdp_server.c
+******************************************************************************/
+bt_status_t sdp_server_init();
+void sdp_server_cleanup();
+bt_status_t create_sdp_record(bluetooth_sdp_record *records, int* record_handles);
+bt_status_t remove_sdp_record(int record_handle);
+void on_create_record_event(int handle);
+void on_remove_record_event(int handle);
+
+// Utility functions:
+int get_sdp_records_size(bluetooth_sdp_record* in_record, int count);
+void copy_sdp_records(bluetooth_sdp_record* in_records,
+ bluetooth_sdp_record* out_records, int count);
+
+
+/*****************************************************************************
+** Static variables
+******************************************************************************/
+
+static btsdp_callbacks_t *bt_sdp_callbacks = NULL;
+
+static void btif_sdp_search_comp_evt(UINT16 event, char *p_param)
+{
+ tBTA_SDP_SEARCH_COMP *evt_data = (tBTA_SDP_SEARCH_COMP*) p_param;
+ bt_bdaddr_t addr;
+ BTIF_TRACE_DEBUG("%s: event = %d", __FUNCTION__, event);
+
+ if (event != BTA_SDP_SEARCH_COMP_EVT)
+ return;
+
+ bdcpy(addr.address, evt_data->remote_addr);
+
+ HAL_CBACK(bt_sdp_callbacks, sdp_search_cb, evt_data->status,
+ &addr, (uint8_t*)(evt_data->uuid.uu.uuid128),
+ evt_data->record_count, evt_data->records);
+}
+
+static void sdp_search_comp_copy_cb(UINT16 event, char *p_dest, char *p_src)
+{
+ tBTA_SDP_SEARCH_COMP *p_dest_data = (tBTA_SDP_SEARCH_COMP *) p_dest;
+ tBTA_SDP_SEARCH_COMP *p_src_data = (tBTA_SDP_SEARCH_COMP *) p_src;
+
+ if (!p_src)
+ return;
+
+ if (event != BTA_SDP_SEARCH_COMP_EVT)
+ return;
+
+ memcpy(p_dest_data, p_src_data, sizeof(tBTA_SDP_SEARCH_COMP));
+
+ copy_sdp_records(p_src_data->records, p_dest_data->records, p_src_data->record_count);
+}
+
+static void sdp_dm_cback(tBTA_SDP_EVT event, tBTA_SDP *p_data, void *user_data)
+{
+ switch (event)
+ {
+ case BTA_SDP_SEARCH_COMP_EVT:
+ {
+ int size = sizeof(tBTA_SDP);
+ size += get_sdp_records_size(p_data->sdp_search_comp.records,
+ p_data->sdp_search_comp.record_count);
+
+ /* need to deep copy the record content */
+ btif_transfer_context(btif_sdp_search_comp_evt, event,
+ (char*)p_data, size, sdp_search_comp_copy_cb);
+ break;
+ }
+ case BTA_SDP_CREATE_RECORD_USER_EVT:
+ {
+ on_create_record_event((int)user_data);
+ break;
+ }
+ case BTA_SDP_REMOVE_RECORD_USER_EVT:
+ {
+ on_remove_record_event((int)user_data);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static bt_status_t init(btsdp_callbacks_t* callbacks)
+{
+ BTIF_TRACE_DEBUG("Sdp Search %s", __FUNCTION__);
+
+ bt_sdp_callbacks = callbacks;
+ sdp_server_init();
+
+ btif_enable_service(BTA_SDP_SERVICE_ID);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static bt_status_t deinit()
+{
+ BTIF_TRACE_DEBUG("Sdp Search %s", __FUNCTION__);
+
+ bt_sdp_callbacks = NULL;
+ sdp_server_cleanup();
+ btif_disable_service(BTA_SDP_SERVICE_ID);
+
+ return BT_STATUS_SUCCESS;
+}
+
+
+static bt_status_t search(bt_bdaddr_t *bd_addr, const uint8_t *uuid)
+{
+ bdstr_t bdstr;
+ tSDP_UUID sdp_uuid;
+ sdp_uuid.len = 16;
+ memcpy(sdp_uuid.uu.uuid128, uuid, sizeof(sdp_uuid.uu.uuid128));
+
+ BTA_SdpSearch(bd_addr->address, &sdp_uuid);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static const btsdp_interface_t sdp_if = {
+ sizeof(btsdp_interface_t),
+ init,
+ deinit,
+ search,
+ create_sdp_record,
+ remove_sdp_record
+};
+
+const btsdp_interface_t *btif_sdp_get_interface(void)
+{
+ BTIF_TRACE_DEBUG("%s", __FUNCTION__);
+ return &sdp_if;
+}
+
+/*******************************************************************************
+**
+** Function btif_sdp_execute_service
+**
+** Description Initializes/Shuts down the service
+**
+** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btif_sdp_execute_service(BOOLEAN b_enable)
+{
+ BTIF_TRACE_DEBUG("%s enable:%d", __FUNCTION__, b_enable);
+
+ if (b_enable)
+ {
+ BTA_SdpEnable(sdp_dm_cback);
+ }
+ else
+ {
+ /* This is called on BT disable so no need to extra cleanup */
+ }
+ return BT_STATUS_SUCCESS;
+}
+
diff --git a/btif/src/btif_sdp_server.c b/btif/src/btif_sdp_server.c
new file mode 100644
index 000000000..ec35cf796
--- /dev/null
+++ b/btif/src/btif_sdp_server.c
@@ -0,0 +1,721 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Samsung System LSI
+ *
+ * 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.
+ *
+ ******************************************************************************/
+
+/************************************************************************************
+ *
+ * Filename: btif_sdp_server.c
+ * Description: SDP server Bluetooth Interface to create and remove SDP records.
+ * To be used in combination with the RFCOMM/L2CAP(LE) sockets.
+ *
+ *
+ ***********************************************************************************/
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sdp.h>
+#include <stdlib.h>
+
+#define LOG_TAG "BTIF_SDP_SERVER"
+#include "btif_common.h"
+#include "btif_util.h"
+#include "bta_sdp_api.h"
+#include "bta_sys.h"
+#include "utl.h"
+#include "btif_sock_util.h"
+
+#if __GLIBC__
+static pthread_mutex_t sdp_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#else
+static pthread_mutex_t sdp_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+#endif
+
+
+/**
+ * The need for a state variable have been reduced to two states.
+ * The remaining state control is handled by program flow
+ */
+typedef enum {
+ SDP_RECORD_FREE = 0,
+ SDP_RECORD_ALLOCED,
+} sdp_state_t;
+
+typedef struct {
+ sdp_state_t state;
+ int sdp_handle;
+ bluetooth_sdp_record* record_data;
+} sdp_slot_t;
+
+#define MAX_SDP_SLOTS 128
+static sdp_slot_t sdp_slots[MAX_SDP_SLOTS];
+
+/*****************************************************************************
+ * LOCAL Functions
+ *****************************************************************************/
+static int add_maps_sdp(const bluetooth_sdp_mas_record* rec);
+static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec);
+static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec);
+static int add_opps_sdp(const bluetooth_sdp_ops_record* rec);
+bt_status_t remove_sdp_record(int record_id);
+static int free_sdp_slot(int id);
+
+/******************************************************************************
+ * WARNING: Functions below are not called in BTU context.
+ * Introduced to make it possible to create SDP records from JAVA with both a
+ * RFCOMM channel and a L2CAP PSM.
+ * Overall architecture:
+ * 1) JAVA calls createRecord() which returns a pseudo ID which at a later
+ * point will be linked to a specific SDP handle.
+ * 2) createRecord() requests the BTU task(thread) to call a callback in SDP
+ * which creates the actual record, and updates the ID<->SDPHandle map
+ * based on the ID beeing passed to BTA as user_data.
+ *****************************************************************************/
+
+static void init_sdp_slots()
+{
+ int i;
+ memset(sdp_slots, 0, sizeof(sdp_slot_t)*MAX_SDP_SLOTS);
+ /* if SDP_RECORD_FREE is zero - no need to set the value */
+ if(SDP_RECORD_FREE != 0) {
+ for(i = 0; i < MAX_SDP_SLOTS; i++)
+ {
+ sdp_slots[i].state = SDP_RECORD_FREE;
+ }
+ }
+}
+
+bt_status_t sdp_server_init()
+{
+ BTIF_TRACE_DEBUG("Sdp Server %s", __FUNCTION__);
+ init_sdp_slots();
+ return BT_STATUS_SUCCESS;
+}
+
+void sdp_server_cleanup()
+{
+ BTIF_TRACE_DEBUG("Sdp Server %s", __FUNCTION__);
+ pthread_mutex_lock(&sdp_lock);
+ int i;
+ for(i = 0; i < MAX_SDP_SLOTS; i++)
+ {
+ /*remove_sdp_record(i); we cannot send messages to the other threads, since they might
+ * have been shut down already. Just do local cleanup.
+ */
+ free_sdp_slot(i);
+ }
+ pthread_mutex_unlock(&sdp_lock);
+}
+
+
+int get_sdp_records_size(bluetooth_sdp_record* in_record, int count) {
+ bluetooth_sdp_record* record = in_record;
+ int records_size = 0;
+ int i;
+ for(i=0; i<count; i++) {
+ record = &in_record[i];
+ records_size += sizeof(bluetooth_sdp_record);
+ records_size += record->hdr.service_name_length;
+ if(record->hdr.service_name_length > 0){
+ records_size++; /* + '\0' termination of string */
+ }
+ records_size += record->hdr.user1_ptr_len;
+ records_size += record->hdr.user2_ptr_len;
+ }
+ return records_size;
+}
+
+/* Deep copy all content of in_records into out_records.
+ * out_records must point to a chunk of memory large enough to contain all
+ * the data. Use getSdpRecordsSize() to calculate the needed size. */
+void copy_sdp_records(bluetooth_sdp_record* in_records,
+ bluetooth_sdp_record* out_records, int count) {
+ int i;
+ bluetooth_sdp_record* in_record;
+ bluetooth_sdp_record* out_record;
+ char* free_ptr = (char*)(&out_records[count]); /* set pointer to after the last entry */
+
+ for(i=0; i<count; i++) {
+ in_record = &in_records[i];
+ out_record = &out_records[i];
+ *out_record = *in_record;
+
+ if(in_record->hdr.service_name == NULL || in_record->hdr.service_name_length == 0) {
+ out_record->hdr.service_name = NULL;
+ out_record->hdr.service_name_length = 0;
+ } else {
+ out_record->hdr.service_name = free_ptr; // Update service_name pointer
+ // Copy string
+ memcpy(free_ptr, in_record->hdr.service_name, in_record->hdr.service_name_length);
+ free_ptr += in_record->hdr.service_name_length;
+ *(free_ptr) = '\0'; // Set '\0' termination of string
+ free_ptr++;
+ }
+ if(in_record->hdr.user1_ptr != NULL) {
+ out_record->hdr.user1_ptr = (UINT8*)free_ptr; // Update pointer
+ memcpy(free_ptr, in_record->hdr.user1_ptr, in_record->hdr.user1_ptr_len); // Copy content
+ free_ptr += in_record->hdr.user1_ptr_len;
+ }
+ if(in_record->hdr.user2_ptr != NULL) {
+ out_record->hdr.user2_ptr = (UINT8*)free_ptr; // Update pointer
+ memcpy(free_ptr, in_record->hdr.user2_ptr, in_record->hdr.user2_ptr_len); // Copy content
+ free_ptr += in_record->hdr.user2_ptr_len;
+ }
+ }
+ return;
+}
+
+/* Reserve a slot in sdp_slots, copy data and set a reference to the copy.
+ * The record_data will contain both the record and any data pointed to by
+ * the record.
+ * Currently this covers:
+ * service_name string,
+ * user1_ptr and
+ * user2_ptr. */
+static int alloc_sdp_slot(bluetooth_sdp_record* in_record) {
+ int i;
+ char* tmp_ptr = NULL;
+ int record_size = get_sdp_records_size(in_record, 1);
+ bluetooth_sdp_record* record = malloc(record_size);
+
+ copy_sdp_records(in_record, record, 1);
+
+ /* We are optimists here, and preallocate the record.
+ * This is to reduce the time we hold the sdp_lock. */
+ pthread_mutex_lock(&sdp_lock);
+ for(i = 0; i < MAX_SDP_SLOTS; i++)
+ {
+ if(sdp_slots[i].state == SDP_RECORD_FREE) {
+ sdp_slots[i].state = SDP_RECORD_ALLOCED;
+ sdp_slots[i].record_data = record;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&sdp_lock);
+ if(i >= MAX_SDP_SLOTS) {
+ APPL_TRACE_ERROR("alloc_sdp_slot failed - no more free slots!");
+ /* Rearly the optimist is too optimistic, and cleanup is needed...*/
+ free(record);
+ return -1;
+ }
+ return i;
+}
+
+static int free_sdp_slot(int id) {
+ int handle = -1;
+ bluetooth_sdp_record* record = NULL;
+ if(id >= MAX_SDP_SLOTS) {
+ APPL_TRACE_ERROR("free_sdp_slot failed - id %d is invalid", id);
+ return handle;
+ }
+ pthread_mutex_lock(&sdp_lock);
+ handle = sdp_slots[id].sdp_handle;
+ sdp_slots[id].sdp_handle = 0;
+ if(sdp_slots[id].state != SDP_RECORD_FREE)
+ {
+ /* safe a copy of the pointer, and free after unlock() */
+ record = sdp_slots[id].record_data;
+ }
+ sdp_slots[id].state = SDP_RECORD_FREE;
+ pthread_mutex_unlock(&sdp_lock);
+
+ if(record != NULL) {
+ free(record);
+ } else {
+ // Record have already been freed
+ handle = -1;
+ }
+ return handle;
+}
+
+/***
+ * Use this to get a reference to a SDP slot AND change the state to
+ * SDP_RECORD_CREATE_INITIATED.
+ */
+static const sdp_slot_t* start_create_sdp(int id) {
+ sdp_slot_t* sdp_slot;
+ if(id >= MAX_SDP_SLOTS) {
+ APPL_TRACE_ERROR("start_create_sdp failed - id %d is invalid", id);
+ return NULL;
+ }
+ pthread_mutex_lock(&sdp_lock);
+ if(sdp_slots[id].state == SDP_RECORD_ALLOCED) {
+ sdp_slot = &(sdp_slots[id]);
+ } else {
+ /* The record have been removed before this event occurred - e.g. deinit */
+ sdp_slot = NULL;
+ }
+ pthread_mutex_unlock(&sdp_lock);
+ if(sdp_slot == NULL) {
+ APPL_TRACE_ERROR("start_create_sdp failed - state for id %d is "
+ "sdp_slots[id].state = %d expected %d",
+ id, sdp_slots[id].state, SDP_RECORD_ALLOCED);
+ }
+ return sdp_slot;
+}
+
+static void set_sdp_handle(int id, int handle) {
+ pthread_mutex_lock(&sdp_lock);
+ sdp_slots[id].sdp_handle = handle;
+ pthread_mutex_unlock(&sdp_lock);
+ BTIF_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x",
+ __FUNCTION__, id, handle);
+
+}
+
+
+bt_status_t create_sdp_record(bluetooth_sdp_record *record, int* record_handle) {
+ int handle;
+
+ handle = alloc_sdp_slot(record);
+ BTIF_TRACE_DEBUG("Sdp Server %s handle = 0x%08x", __FUNCTION__, handle);
+
+ if(handle < 0)
+ return BT_STATUS_FAIL;
+
+ BTA_SdpCreateRecordByUser((void*) handle);
+
+ *record_handle = handle;
+
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t remove_sdp_record(int record_id) {
+ int handle;
+
+ /* Get the Record handle, and free the slot */
+ handle = free_sdp_slot(record_id);
+ BTIF_TRACE_DEBUG("Sdp Server %s id=%d to handle=0x%08x",
+ __FUNCTION__, record_id, handle);
+
+ /* Pass the actual record handle */
+ if(handle > 0) {
+ BTA_SdpRemoveRecordByUser((void*) handle);
+ return BT_STATUS_SUCCESS;
+ }
+ BTIF_TRACE_DEBUG("Sdp Server %s - record already removed - or never created", __FUNCTION__);
+ return BT_STATUS_FAIL;
+}
+
+
+/******************************************************************************
+ * CALLBACK FUNCTIONS
+ * Called in BTA context to create/remove SDP records.
+ ******************************************************************************/
+
+void on_create_record_event(int id) {
+ /*
+ * 1) Fetch the record pointer, and change its state?
+ * 2) switch on the type to create the correct record
+ * 3) Update state on completion
+ * 4) What to do at fail?
+ * */
+ BTIF_TRACE_DEBUG("Sdp Server %s", __FUNCTION__);
+ const sdp_slot_t* sdp_slot = start_create_sdp(id);
+ /* In the case we are shutting down, sdp_slot is NULL */
+ if(sdp_slot != NULL) {
+ bluetooth_sdp_record* record = sdp_slot->record_data;
+ int handle = -1;
+ switch(record->hdr.type) {
+ case SDP_TYPE_MAP_MAS:
+ handle = add_maps_sdp(&record->mas);
+ break;
+ case SDP_TYPE_MAP_MNS:
+ handle = add_mapc_sdp(&record->mns);
+ break;
+ case SDP_TYPE_PBAP_PSE:
+ handle = add_pbaps_sdp(&record->pse);
+ break;
+ case SDP_TYPE_OPP_SERVER:
+ handle = add_opps_sdp(&record->ops);
+ break;
+ case SDP_TYPE_PBAP_PCE:
+ // break; not yet supported
+ default:
+ BTIF_TRACE_DEBUG("Record type %d is not supported",record->hdr.type);
+ break;
+ }
+ if(handle != -1) {
+ set_sdp_handle(id, handle);
+ }
+ }
+}
+
+void on_remove_record_event(int handle) {
+ BTIF_TRACE_DEBUG("Sdp Server %s", __FUNCTION__);
+
+ // User data carries the actual SDP handle, not the ID.
+ if(handle != -1 && handle != 0) {
+ BOOLEAN result;
+ result = SDP_DeleteRecord( handle );
+ if(result == FALSE) {
+ BTIF_TRACE_ERROR(" Unable to remove handle 0x%08x", handle);
+ }
+ }
+}
+
+/****
+ * Below the actual functions accessing BTA context data - hence only call from BTA context!
+ */
+
+/* Create a MAP MAS SDP record based on information stored in a bluetooth_sdp_mas_record */
+static int add_maps_sdp(const bluetooth_sdp_mas_record* rec)
+{
+
+ tSDP_PROTOCOL_ELEM protoList [3];
+ UINT16 service = UUID_SERVCLASS_MESSAGE_ACCESS;
+ UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
+ BOOLEAN status = TRUE;
+ UINT32 sdp_handle = 0;
+ UINT8 temp[4];
+ UINT8* p_temp = temp;
+
+ APPL_TRACE_DEBUG("add_mas_sdp: MASID = 0x%02x, scn 0x%02x, psm = 0x%04x\n service name %s",
+ rec->mas_instance_id, rec->hdr.rfcomm_channel_number,
+ rec->hdr.l2cap_psm, rec->hdr.service_name);
+
+ APPL_TRACE_DEBUG(" msg_types: 0x%02x, feature_bits: 0x%08x",
+ rec->supported_message_types, rec->supported_features);
+
+ if ((sdp_handle = SDP_CreateRecord()) == 0)
+ {
+ APPL_TRACE_ERROR("MAPS SDP: Unable to register MAPS Service");
+ return sdp_handle;
+ }
+
+ /* add service class */
+ status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service);
+ memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) );
+
+ /* add protocol list, including RFCOMM scn */
+ protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
+ protoList[0].num_params = 0;
+ protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
+ protoList[1].num_params = 1;
+ protoList[1].params[0] = rec->hdr.rfcomm_channel_number;
+ protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX;
+ protoList[2].num_params = 0;
+ status &= SDP_AddProtocolList(sdp_handle, 3, protoList);
+
+ /* Add a name entry */
+ status &= SDP_AddAttribute(sdp_handle,
+ (UINT16)ATTR_ID_SERVICE_NAME,
+ (UINT8)TEXT_STR_DESC_TYPE,
+ (UINT32)(rec->hdr.service_name_length + 1),
+ (UINT8 *)rec->hdr.service_name);
+
+ /* Add in the Bluetooth Profile Descriptor List */
+ status &= SDP_AddProfileDescriptorList(sdp_handle,
+ UUID_SERVCLASS_MAP_PROFILE,
+ rec->hdr.profile_version);
+
+ /* Add MAS instance ID */
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAS_INSTANCE_ID, UINT_DESC_TYPE,
+ (UINT32)1, (UINT8*)&rec->mas_instance_id);
+
+ /* Add supported message types */
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_MSG_TYPE, UINT_DESC_TYPE,
+ (UINT32)1, (UINT8*)&rec->supported_message_types);
+
+ /* Add supported feature */
+ UINT32_TO_BE_STREAM(p_temp, rec->supported_features);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES,
+ UINT_DESC_TYPE, (UINT32)4, temp);
+
+ /* Add the L2CAP PSM if present */
+ if(rec->hdr.l2cap_psm != -1) {
+ p_temp = temp;// The macro modifies p_temp, hence rewind.
+ UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM,
+ UINT_DESC_TYPE, (UINT32)2, temp);
+ }
+
+ /* Make the service browseable */
+ status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
+
+ if (!status)
+ {
+ SDP_DeleteRecord(sdp_handle);
+ sdp_handle = 0;
+ APPL_TRACE_ERROR("add_maps_sdp FAILED");
+ }
+ else
+ {
+ bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */
+ APPL_TRACE_DEBUG("MAPS: SDP Registered (handle 0x%08x)", sdp_handle);
+ }
+ return sdp_handle;
+}
+
+/* Create a MAP MNS SDP record based on information stored in a bluetooth_sdp_mns_record */
+static int add_mapc_sdp(const bluetooth_sdp_mns_record* rec)
+{
+
+ tSDP_PROTOCOL_ELEM protoList [3];
+ UINT16 service = UUID_SERVCLASS_MESSAGE_NOTIFICATION;
+ UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
+ BOOLEAN status = TRUE;
+ UINT32 sdp_handle = 0;
+ UINT8 temp[4];
+ UINT8* p_temp = temp;
+
+ APPL_TRACE_DEBUG("add_mas_sdp: scn 0x%02x, psm = 0x%04x\n service name %s",
+ rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name);
+
+ APPL_TRACE_DEBUG(" feature_bits: 0x%08x", rec->supported_features);
+
+ if ((sdp_handle = SDP_CreateRecord()) == 0)
+ {
+ APPL_TRACE_ERROR("add_mapc_sdp: Unable to register MAP Notification Service");
+ return sdp_handle;
+ }
+
+ /* add service class */
+ status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service);
+ memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) );
+
+ /* add protocol list, including RFCOMM scn */
+ protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
+ protoList[0].num_params = 0;
+ protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
+ protoList[1].num_params = 1;
+ protoList[1].params[0] = rec->hdr.rfcomm_channel_number;
+ protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX;
+ protoList[2].num_params = 0;
+ status &= SDP_AddProtocolList(sdp_handle, 3, protoList);
+
+ /* Add a name entry */
+ status &= SDP_AddAttribute(sdp_handle,
+ (UINT16)ATTR_ID_SERVICE_NAME,
+ (UINT8)TEXT_STR_DESC_TYPE,
+ (UINT32)(rec->hdr.service_name_length + 1),
+ (UINT8 *)rec->hdr.service_name);
+
+ /* Add in the Bluetooth Profile Descriptor List */
+ status &= SDP_AddProfileDescriptorList(sdp_handle,
+ UUID_SERVCLASS_MAP_PROFILE,
+ rec->hdr.profile_version);
+
+ /* Add supported feature */
+ UINT32_TO_BE_STREAM(p_temp, rec->supported_features);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_MAP_SUPPORTED_FEATURES,
+ UINT_DESC_TYPE, (UINT32)4, temp);
+
+ /* Add the L2CAP PSM if present */
+ if(rec->hdr.l2cap_psm != -1) {
+ p_temp = temp;// The macro modifies p_temp, hence rewind.
+ UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM,
+ UINT_DESC_TYPE, (UINT32)2, temp);
+ }
+
+ /* Make the service browseable */
+ status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
+
+ if (!status)
+ {
+ SDP_DeleteRecord(sdp_handle);
+ sdp_handle = 0;
+ APPL_TRACE_ERROR("add_mapc_sdp FAILED");
+ }
+ else
+ {
+ bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */
+ APPL_TRACE_DEBUG("MAPC: SDP Registered (handle 0x%08x)", sdp_handle);
+ }
+ return sdp_handle;
+}
+
+/* Create a PBAP Server SDP record based on information stored in a bluetooth_sdp_pse_record */
+static int add_pbaps_sdp(const bluetooth_sdp_pse_record* rec)
+{
+
+ tSDP_PROTOCOL_ELEM protoList [3];
+ UINT16 service = UUID_SERVCLASS_PBAP_PSE;
+ UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
+ BOOLEAN status = TRUE;
+ UINT32 sdp_handle = 0;
+ UINT8 temp[4];
+ UINT8* p_temp = temp;
+
+ APPL_TRACE_DEBUG("add_pbaps_sdp: scn 0x%02x, psm = 0x%04x\n service name %s",
+ rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name);
+
+ APPL_TRACE_DEBUG(" supported_repositories: 0x%08x, feature_bits: 0x%08x",
+ rec->supported_repositories, rec->supported_features);
+
+ if ((sdp_handle = SDP_CreateRecord()) == 0)
+ {
+ APPL_TRACE_ERROR("add_pbaps_sdp: Unable to register PBAP Server Service");
+ return sdp_handle;
+ }
+
+ /* add service class */
+ status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service);
+ memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) );
+
+ /* add protocol list, including RFCOMM scn */
+ protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
+ protoList[0].num_params = 0;
+ protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
+ protoList[1].num_params = 1;
+ protoList[1].params[0] = rec->hdr.rfcomm_channel_number;
+ protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX;
+ protoList[2].num_params = 0;
+ status &= SDP_AddProtocolList(sdp_handle, 3, protoList);
+
+ /* Add a name entry */
+ status &= SDP_AddAttribute(sdp_handle,
+ (UINT16)ATTR_ID_SERVICE_NAME,
+ (UINT8)TEXT_STR_DESC_TYPE,
+ (UINT32)(rec->hdr.service_name_length + 1),
+ (UINT8 *)rec->hdr.service_name);
+
+ /* Add in the Bluetooth Profile Descriptor List */
+ status &= SDP_AddProfileDescriptorList(sdp_handle,
+ UUID_SERVCLASS_PHONE_ACCESS,
+ rec->hdr.profile_version);
+
+ /* Add supported repositories 1 byte */
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_REPOSITORIES,
+ UINT_DESC_TYPE, (UINT32)1, (UINT8*)&rec->supported_repositories);
+
+ /* Add supported feature 4 bytes*/
+ UINT32_TO_BE_STREAM(p_temp, rec->supported_features);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_PBAP_SUPPORTED_FEATURES,
+ UINT_DESC_TYPE, (UINT32)4, temp);
+
+ /* Add the L2CAP PSM if present */
+ if(rec->hdr.l2cap_psm != -1) {
+ p_temp = temp;// The macro modifies p_temp, hence rewind.
+ UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM,
+ UINT_DESC_TYPE, (UINT32)2, temp);
+ }
+
+ /* Make the service browseable */
+ status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
+
+ if (!status)
+ {
+ SDP_DeleteRecord(sdp_handle);
+ sdp_handle = 0;
+ APPL_TRACE_ERROR("add_pbaps_sdp FAILED");
+ }
+ else
+ {
+ bta_sys_add_uuid(service); /* UUID_SERVCLASS_MESSAGE_ACCESS */
+ APPL_TRACE_DEBUG("PBAPS: SDP Registered (handle 0x%08x)", sdp_handle);
+ }
+ return sdp_handle;
+}
+
+
+/* Create a OPP Server SDP record based on information stored in a bluetooth_sdp_ops_record */
+static int add_opps_sdp(const bluetooth_sdp_ops_record* rec)
+{
+
+ tSDP_PROTOCOL_ELEM protoList [3];
+ UINT16 service = UUID_SERVCLASS_OBEX_OBJECT_PUSH;
+ UINT16 browse = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
+ UINT8 type_len[rec->supported_formats_list_len];
+ UINT8 desc_type[rec->supported_formats_list_len];
+ UINT8 *type_value[rec->supported_formats_list_len];
+ BOOLEAN status = TRUE;
+ UINT32 sdp_handle = 0;
+ UINT8 temp[4];
+ UINT8* p_temp = temp;
+ tBTA_UTL_COD cod;
+ int i,j;
+
+ APPL_TRACE_DEBUG("add_opps_sdp: scn 0x%02x, psm = 0x%04x\n service name %s",
+ rec->hdr.rfcomm_channel_number, rec->hdr.l2cap_psm, rec->hdr.service_name);
+
+ APPL_TRACE_DEBUG(" supported formats count: %d",
+ rec->supported_formats_list_len);
+
+ if ((sdp_handle = SDP_CreateRecord()) == 0)
+ {
+ APPL_TRACE_ERROR("add_opps_sdp: Unable to register Object Push Server Service");
+ return sdp_handle;
+ }
+
+ /* add service class */
+ status &= SDP_AddServiceClassIdList(sdp_handle, 1, &service);
+ memset( protoList, 0 , 3*sizeof(tSDP_PROTOCOL_ELEM) );
+
+ /* add protocol list, including RFCOMM scn */
+ protoList[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
+ protoList[0].num_params = 0;
+ protoList[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
+ protoList[1].num_params = 1;
+ protoList[1].params[0] = rec->hdr.rfcomm_channel_number;
+ protoList[2].protocol_uuid = UUID_PROTOCOL_OBEX;
+ protoList[2].num_params = 0;
+ status &= SDP_AddProtocolList(sdp_handle, 3, protoList);
+
+ /* Add a name entry */
+ status &= SDP_AddAttribute(sdp_handle,
+ (UINT16)ATTR_ID_SERVICE_NAME,
+ (UINT8)TEXT_STR_DESC_TYPE,
+ (UINT32)(rec->hdr.service_name_length + 1),
+ (UINT8 *)rec->hdr.service_name);
+
+ /* Add in the Bluetooth Profile Descriptor List */
+ status &= SDP_AddProfileDescriptorList(sdp_handle,
+ UUID_SERVCLASS_OBEX_OBJECT_PUSH,
+ rec->hdr.profile_version);
+
+ /* add sequence for supported types */
+ for (i = 0, j = 0; i < rec->supported_formats_list_len; i++)
+ {
+ type_value[j] = (UINT8 *) &rec->supported_formats_list[i];
+ desc_type[j] = UINT_DESC_TYPE;
+ type_len[j++] = 1;
+ }
+
+ status &= SDP_AddSequence(sdp_handle, (UINT16) ATTR_ID_SUPPORTED_FORMATS_LIST,
+ (UINT8) rec->supported_formats_list_len, desc_type, type_len, type_value);
+
+ /* Add the L2CAP PSM if present */
+ if(rec->hdr.l2cap_psm != -1) {
+ p_temp = temp;// The macro modifies p_temp, hence rewind.
+ UINT16_TO_BE_STREAM(p_temp, rec->hdr.l2cap_psm);
+ status &= SDP_AddAttribute(sdp_handle, ATTR_ID_GOEP_L2CAP_PSM,
+ UINT_DESC_TYPE, (UINT32)2, temp);
+ }
+
+ /* Make the service browseable */
+ status &= SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse);
+
+ if (!status)
+ {
+ SDP_DeleteRecord(sdp_handle);
+ sdp_handle = 0;
+ APPL_TRACE_ERROR("add_opps_sdp FAILED");
+ }
+ else
+ {
+ /* set class of device */
+ cod.service = BTM_COD_SERVICE_OBJ_TRANSFER;
+ utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS);
+
+ bta_sys_add_uuid(service); /* UUID_SERVCLASS_OBEX_OBJECT_PUSH */
+ APPL_TRACE_DEBUG("OPPS: SDP Registered (handle 0x%08x)", sdp_handle);
+ }
+ return sdp_handle;
+}
+
+
diff --git a/btif/src/btif_sock.c b/btif/src/btif_sock.c
index 831ae7341..c441eebce 100644
--- a/btif/src/btif_sock.c
+++ b/btif/src/btif_sock.c
@@ -27,9 +27,9 @@
#include "btif_sock_rfc.h"
#include "btif_sock_sco.h"
#include "btif_sock_thread.h"
+#include "btif_sock_l2cap.h"
+#include "btif_sock_sdp.h"
#include "btif_util.h"
-#include "osi/include/osi.h"
-#include "osi/include/log.h"
#include "osi/include/thread.h"
static bt_status_t btsock_listen(btsock_type_t type, const char *service_name, const uint8_t *uuid, int channel, int *sock_fd, int flags);
@@ -47,6 +47,7 @@ btsock_interface_t *btif_sock_get_interface(void) {
btsock_connect
};
+
return &interface;
}
@@ -67,6 +68,12 @@ bt_status_t btif_sock_init(void) {
goto error;
}
+ status = btsock_l2cap_init(thread_handle);
+ if (status != BT_STATUS_SUCCESS) {
+ LOG_ERROR("%s error initializing L2CAP sockets: %d", __func__, status);
+ goto error;
+ }
+
thread = thread_new("btif_sock");
if (!thread) {
LOG_ERROR("%s error creating new thread.", __func__);
@@ -101,14 +108,17 @@ void btif_sock_cleanup(void) {
btsock_thread_exit(thread_handle);
btsock_rfc_cleanup();
btsock_sco_cleanup();
+ btsock_l2cap_cleanup();
thread_free(thread);
thread_handle = -1;
thread = NULL;
}
static bt_status_t btsock_listen(btsock_type_t type, const char *service_name, const uint8_t *service_uuid, int channel, int *sock_fd, int flags) {
- assert(service_uuid != NULL || channel > 0);
- assert(sock_fd != NULL);
+ if((flags & BTSOCK_FLAG_NO_SDP) == 0) {
+ assert(service_uuid != NULL || channel > 0);
+ assert(sock_fd != NULL);
+ }
*sock_fd = INVALID_FD;
bt_status_t status = BT_STATUS_FAIL;
@@ -117,6 +127,9 @@ static bt_status_t btsock_listen(btsock_type_t type, const char *service_name, c
case BTSOCK_RFCOMM:
status = btsock_rfc_listen(service_name, service_uuid, channel, sock_fd, flags);
break;
+ case BTSOCK_L2CAP:
+ status = btsock_l2cap_listen(service_name, channel, sock_fd, flags);
+ break;
case BTSOCK_SCO:
status = btsock_sco_listen(sock_fd, flags);
@@ -143,6 +156,10 @@ static bt_status_t btsock_connect(const bt_bdaddr_t *bd_addr, btsock_type_t type
status = btsock_rfc_connect(bd_addr, uuid, channel, sock_fd, flags);
break;
+ case BTSOCK_L2CAP:
+ status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags);
+ break;
+
case BTSOCK_SCO:
status = btsock_sco_connect(bd_addr, sock_fd, flags);
break;
@@ -160,7 +177,9 @@ static void btsock_signaled(int fd, int type, int flags, uint32_t user_id) {
case BTSOCK_RFCOMM:
btsock_rfc_signaled(fd, flags, user_id);
break;
-
+ case BTSOCK_L2CAP:
+ btsock_l2cap_signaled(fd, flags, user_id);
+ break;
default:
assert(false && "Invalid socket type");
break;
diff --git a/btif/src/btif_sock_l2cap.c b/btif/src/btif_sock_l2cap.c
new file mode 100644
index 000000000..8e5ef1d74
--- /dev/null
+++ b/btif/src/btif_sock_l2cap.c
@@ -0,0 +1,1070 @@
+/*
+* Copyright (C) 2014 Samsung System LSI
+* Copyright (C) 2013 The Android Open Source Project
+*
+* 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.
+*/
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <pthread.h>
+
+#define LOG_TAG "BTIF_SOCK"
+#include "btif_common.h"
+#include "btif_util.h"
+
+#include "bta_api.h"
+#include "btif_sock_thread.h"
+#include "btif_sock_sdp.h"
+#include "btif_sock_util.h"
+#include "btif_sock_l2cap.h"
+#include "l2cdefs.h"
+
+#include "bt_target.h"
+#include "gki.h"
+#include "hcimsgs.h"
+#include "sdp_api.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "bta_jv_api.h"
+#include "bta_jv_co.h"
+#include "port_api.h"
+#include "l2c_api.h"
+
+#include <cutils/log.h>
+#include <hardware/bluetooth.h>
+#define asrt(s) if (!(s)) APPL_TRACE_ERROR("## %s assert %s failed at line:%d ##",__FUNCTION__, \
+ #s, __LINE__)
+
+#if __GLIBC__
+static pthread_mutex_t slot_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#else
+static pthread_mutex_t slot_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+#endif
+
+
+struct packet {
+ struct packet *next, *prev;
+ uint32_t len;
+ uint8_t *data;
+};
+
+typedef struct l2cap_socket {
+
+ struct l2cap_socket *prev; //link to prev list item
+ struct l2cap_socket *next; //link to next list item
+ bt_bdaddr_t addr; //other side's address
+ char name[256]; //user-friendly name of the service
+ uint32_t id; //just a tag to find this struct
+ int handle; //handle from lower layers
+ unsigned security; //security flags
+ int channel; //channel (fixed_chan) or PSM (!fixed_chan)
+ int our_fd; //fd from our side
+ int app_fd; //fd from app's side
+
+ unsigned bytes_buffered;
+ struct packet *first_packet; //fist packet to be delivered to app
+ struct packet *last_packet; //last packet to be delivered to app
+
+ BUFFER_Q incoming_que; //data that came in but has not yet been read
+ unsigned fixed_chan :1; //fixed channel (or psm?)
+ unsigned server :1; //is a server? (or connecting?)
+ unsigned connected :1; //is connected?
+ unsigned outgoing_congest :1; //should we hold?
+ unsigned server_psm_sent :1; //The server shall only send PSM once.
+}l2cap_socket;
+
+static bt_status_t btSock_start_l2cap_server_l(l2cap_socket *sock);
+
+static pthread_mutex_t state_lock;
+
+l2cap_socket *socks = NULL;
+static int pth = -1;
+
+static void btsock_l2cap_cbk(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data);
+
+/* TODO: Consider to remove this buffer, as we have a buffer in l2cap as well, and we risk
+ * a buffer overflow with this implementation if the socket data is not read from
+ * JAVA for a while. In such a case we should use flow control to tell the sender to
+ * back off.
+ * BUT remember we need to avoid blocking the BTA task execution - hence we cannot
+ * directly write to the socket.
+ * we should be able to change to store the data pointer here, and just wait
+ * confirming the l2cap_ind until we have more space in the buffer. */
+
+/* returns FALSE if none - caller must free "data" memory when done with it */
+static char packet_get_head_l(l2cap_socket *sock, uint8_t **data, uint32_t *len)
+{
+ struct packet *p = sock->first_packet;
+
+ if (!p)
+ return FALSE;
+
+ if (data)
+ *data = sock->first_packet->data;
+ if (len)
+ *len = sock->first_packet->len;
+ sock->first_packet = p->next;
+ if (sock->first_packet)
+ sock->first_packet->prev = NULL;
+ else
+ sock->last_packet = NULL;
+
+ if(len)
+ sock->bytes_buffered -= *len;
+
+ free(p);
+
+ return TRUE;
+}
+
+static struct packet *packet_alloc(const uint8_t *data, uint32_t len)
+{
+ struct packet *p = calloc(1, sizeof(*p));
+ uint8_t *buf = malloc(len);
+
+ if (p && buf) {
+
+ p->data = buf;
+ p->len = len;
+ memcpy(p->data, data, len);
+ return p;
+
+ } else if (p)
+ free(p);
+ else if (buf)
+ free(buf);
+
+ return NULL;
+}
+
+/* makes a copy of the data, returns TRUE on success */
+static char packet_put_head_l(l2cap_socket *sock, const void *data, uint32_t len)
+{
+ struct packet *p = packet_alloc((const uint8_t*)data, len);
+
+ /*
+ * We do not check size limits here since this is used to undo "getting" a
+ * packet that the user read incompletely. That is to say the packet was
+ * already in the queue. We do check thos elimits in packet_put_tail_l() since
+ * that function is used to put new data into the queue.
+ */
+
+ if (!p)
+ return FALSE;
+
+ p->prev = NULL;
+ p->next = sock->first_packet;
+ sock->first_packet = p;
+ if (p->next)
+ p->next->prev = p;
+ else
+ sock->last_packet = p;
+
+ sock->bytes_buffered += len;
+
+ return TRUE;
+}
+
+/* makes a copy of the data, returns TRUE on success */
+static char packet_put_tail_l(l2cap_socket *sock, const void *data, uint32_t len)
+{
+ struct packet *p = packet_alloc((const uint8_t*)data, len);
+
+ if (sock->bytes_buffered >= L2CAP_MAX_RX_BUFFER) {
+ ALOGE("packet_put_tail_l: buffer overflow");
+ return FALSE;
+ }
+
+ if (!p) {
+ ALOGE("packet_put_tail_l: unable to allocate packet...");
+ return FALSE;
+ }
+
+ p->next = NULL;
+ p->prev = sock->last_packet;
+ sock->last_packet = p;
+ if (p->prev)
+ p->prev->next = p;
+ else
+ sock->first_packet = p;
+
+ sock->bytes_buffered += len;
+
+ return TRUE;
+}
+
+static inline void bd_copy(UINT8* dest, UINT8* src, BOOLEAN swap)
+{
+ if (swap) {
+ int i;
+ for (i =0; i < 6 ;i++)
+ dest[i]= src[5-i];
+ }
+ else memcpy(dest, src, 6);
+}
+
+static char is_inited(void)
+{
+ char ret;
+
+
+ pthread_mutex_lock(&state_lock);
+ ret = pth != -1;
+ pthread_mutex_unlock(&state_lock);
+
+ return ret;
+}
+
+/* only call with mutex taken */
+static l2cap_socket *btsock_l2cap_find_by_id_l(uint32_t id)
+{
+ l2cap_socket *sock = socks;
+
+ while (sock && sock->id != id)
+ sock = sock->next;
+
+ return sock;
+}
+
+static void btsock_l2cap_free_l(l2cap_socket *sock)
+{
+ uint8_t *buf;
+ l2cap_socket *t = socks;
+
+ while(t && t != sock)
+ t = t->next;
+
+ if (!t) /* prever double-frees */
+ return;
+
+ if (sock->next)
+ sock->next->prev = sock->prev;
+
+ if (sock->prev)
+ sock->prev->next = sock->next;
+ else
+ socks = sock->next;
+
+ shutdown(sock->our_fd, SHUT_RDWR);
+ close(sock->our_fd);
+ if (sock->app_fd != -1) {
+ close(sock->app_fd);
+ } else {
+ APPL_TRACE_ERROR("SOCK_LIST: free(id = %d) - NO app_fd!", sock->id);
+ }
+
+ while (packet_get_head_l(sock, &buf, NULL))
+ free(buf);
+
+ //lower-level close() should be idempotent... so let's call it and see...
+ // Only call if we are non server connections
+ if (sock->handle && (sock->server == FALSE)) {
+ if (sock->fixed_chan)
+ BTA_JvL2capCloseLE(sock->handle);
+ else
+ BTA_JvL2capClose(sock->handle);
+ }
+ if ((sock->channel >= 0) && (sock->server == TRUE)) {
+ if (sock->fixed_chan) {
+ BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP_LE);
+ } else {
+ BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP);
+ }
+ }
+
+ APPL_TRACE_DEBUG("SOCK_LIST: free(id = %d)", sock->id);
+ free(sock);
+}
+
+static void btsock_l2cap_free(l2cap_socket *sock)
+{
+ pthread_mutex_lock(&state_lock);
+ btsock_l2cap_free_l(sock);
+ pthread_mutex_unlock(&state_lock);
+}
+
+static l2cap_socket *btsock_l2cap_alloc_l(const char *name, const bt_bdaddr_t *addr,
+ char is_server, int flags)
+{
+ l2cap_socket *sock;
+ unsigned security = 0;
+ int fds[2];
+
+ if (flags & BTSOCK_FLAG_ENCRYPT)
+ security |= is_server ? BTM_SEC_IN_ENCRYPT : BTM_SEC_OUT_ENCRYPT;
+ if (flags & BTSOCK_FLAG_AUTH)
+ security |= is_server ? BTM_SEC_IN_AUTHENTICATE : BTM_SEC_OUT_AUTHENTICATE;
+
+ sock = calloc(1, sizeof(*sock));
+ if (!sock) {
+ APPL_TRACE_ERROR("alloc failed");
+ goto fail_alloc;
+ }
+
+ if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, fds)) {
+ APPL_TRACE_ERROR("socketpair failed, errno:%d", errno);
+ goto fail_sockpair;
+ }
+
+ sock->our_fd = fds[0];
+ sock->app_fd = fds[1];
+ sock->security = security;
+ sock->server = is_server;
+ sock->connected = FALSE;
+ sock->handle = 0;
+ sock->server_psm_sent = FALSE;
+
+ if (name)
+ strncpy(sock->name, name, sizeof(sock->name) - 1);
+ if (addr)
+ sock->addr = *addr;
+
+ sock->first_packet = NULL;
+ sock->last_packet = NULL;
+
+ sock->next = socks;
+ sock->prev = NULL;
+ if (socks)
+ socks->prev = sock;
+ sock->id = (socks ? socks->id : 0) + 1;
+ socks = sock;
+ /* paranoia cap on: verify no ID duplicates due to overflow and fix as needed */
+ while (1) {
+ l2cap_socket *t;
+ t = socks->next;
+ while (t && t->id != sock->id) {
+ t = t->next;
+ }
+ if (!t && sock->id) /* non-zeor handle is unique -> we're done */
+ break;
+ /* if we're here, we found a duplicate */
+ if (!++sock->id) /* no zero IDs allowed */
+ sock->id++;
+ }
+ APPL_TRACE_DEBUG("SOCK_LIST: alloc(id = %d)", sock->id);
+ return sock;
+
+fail_sockpair:
+ free(sock);
+
+fail_alloc:
+ return NULL;
+}
+
+static l2cap_socket *btsock_l2cap_alloc(const char *name, const bt_bdaddr_t *addr,
+ char is_server, int flags)
+{
+ l2cap_socket *ret;
+
+ pthread_mutex_lock(&state_lock);
+ ret = btsock_l2cap_alloc_l(name, addr, is_server, flags);
+ pthread_mutex_unlock(&state_lock);
+
+ return ret;
+}
+
+bt_status_t btsock_l2cap_init(int handle)
+{
+ APPL_TRACE_DEBUG("btsock_l2cap_init...");
+ pthread_mutex_lock(&state_lock);
+ pth = handle;
+ socks = NULL;
+ pthread_mutex_unlock(&state_lock);
+
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t btsock_l2cap_cleanup()
+{
+ pthread_mutex_lock(&state_lock);
+ pth = -1;
+ while (socks)
+ btsock_l2cap_free_l(socks);
+ pthread_mutex_unlock(&state_lock);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static inline BOOLEAN send_app_psm_or_chan_l(l2cap_socket *sock)
+{
+ return sock_send_all(sock->our_fd, (const uint8_t*)&sock->channel, sizeof(sock->channel))
+ == sizeof(sock->channel);
+}
+
+static BOOLEAN send_app_connect_signal(int fd, const bt_bdaddr_t* addr,
+ int channel, int status, int send_fd, int tx_mtu)
+{
+ sock_connect_signal_t cs;
+ cs.size = sizeof(cs);
+ cs.bd_addr = *addr;
+ cs.channel = channel;
+ cs.status = status;
+ cs.max_rx_packet_size = L2CAP_MAX_SDU_LENGTH;
+ cs.max_tx_packet_size = tx_mtu;
+ if (send_fd != -1) {
+ if (sock_send_fd(fd, (const uint8_t*)&cs, sizeof(cs), send_fd) == sizeof(cs))
+ return TRUE;
+ else APPL_TRACE_ERROR("sock_send_fd failed, fd:%d, send_fd:%d", fd, send_fd);
+ } else if (sock_send_all(fd, (const uint8_t*)&cs, sizeof(cs)) == sizeof(cs)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void on_srv_l2cap_listen_started(tBTA_JV_L2CAP_START *p_start, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock) {
+ if (p_start->status != BTA_JV_SUCCESS) {
+ APPL_TRACE_ERROR("Error starting l2cap_listen - status: 0x%04x", p_start->status);
+ btsock_l2cap_free_l(sock);
+ }
+ else {
+ sock->handle = p_start->handle;
+ APPL_TRACE_DEBUG("on_srv_l2cap_listen_started() sock->handle =%d id:%d",
+ sock->handle, sock->id);
+ if(sock->server_psm_sent == FALSE) {
+ if (!send_app_psm_or_chan_l(sock)) {
+ //closed
+ APPL_TRACE_DEBUG("send_app_psm() failed, close rs->id:%d", sock->id);
+ btsock_l2cap_free_l(sock);
+ } else {
+ sock->server_psm_sent = TRUE;
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void on_cl_l2cap_init(tBTA_JV_L2CAP_CL_INIT *p_init, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock) {
+ if (p_init->status != BTA_JV_SUCCESS) {
+ btsock_l2cap_free_l(sock);
+ } else {
+ sock->handle = p_init->handle;
+ }
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+/**
+ * Here we allocate a new sock instance to mimic the BluetoothSocket. The socket will be a clone
+ * of the sock representing the BluetoothServerSocket.
+ * */
+static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN *p_open, l2cap_socket *sock)
+{
+ l2cap_socket *accept_rs;
+ uint32_t new_listen_id;
+
+ // Mutex locked by caller
+ accept_rs = btsock_l2cap_alloc_l(sock->name, (const bt_bdaddr_t*)p_open->rem_bda, FALSE, 0);
+ accept_rs->connected = TRUE;
+ accept_rs->security = sock->security;
+ accept_rs->fixed_chan = sock->fixed_chan;
+ accept_rs->channel = sock->channel;
+ accept_rs->handle = sock->handle;
+ sock->handle = -1; /* We should no longer associate this handle with the server socket */
+
+ /* Swap IDs to hand over the GAP connection to the accepted socket, and start a new server on
+ the newly create socket ID. */
+ new_listen_id = accept_rs->id;
+ accept_rs->id = sock->id;
+ sock->id = new_listen_id;
+
+ if (accept_rs) {
+ //start monitor the socket
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id);
+ btsock_thread_add_fd(pth, accept_rs->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD,
+ accept_rs->id);
+ APPL_TRACE_DEBUG("sending connect signal & app fd: %d to app server to accept() the"
+ " connection", accept_rs->app_fd);
+ APPL_TRACE_DEBUG("server fd:%d, scn:%d", sock->our_fd, sock->channel);
+ send_app_connect_signal(sock->our_fd, &accept_rs->addr, sock->channel, 0,
+ accept_rs->app_fd, p_open->tx_mtu);
+ accept_rs->app_fd = -1; // The fd is closed after sent to app in send_app_connect_signal()
+ // But for some reason we still leak a FD - either the server socket
+ // one or the accept socket one.
+ if(btSock_start_l2cap_server_l(sock) != BT_STATUS_SUCCESS) {
+ btsock_l2cap_free_l(sock);
+ }
+ }
+}
+
+static void on_srv_l2cap_le_connect_l(tBTA_JV_L2CAP_LE_OPEN *p_open, l2cap_socket *sock)
+{
+ l2cap_socket *accept_rs;
+ uint32_t new_listen_id;
+
+ // mutex locked by caller
+ accept_rs = btsock_l2cap_alloc_l(sock->name, (const bt_bdaddr_t*)p_open->rem_bda, FALSE, 0);
+ if (accept_rs) {
+
+ //swap IDs
+ new_listen_id = accept_rs->id;
+ accept_rs->id = sock->id;
+ sock->id = new_listen_id;
+
+ accept_rs->handle = p_open->handle;
+ accept_rs->connected = TRUE;
+ accept_rs->security = sock->security;
+ accept_rs->fixed_chan = sock->fixed_chan;
+ accept_rs->channel = sock->channel;
+
+ //if we do not set a callback, this socket will be dropped */
+ *(p_open->p_p_cback) = (void*)btsock_l2cap_cbk;
+ *(p_open->p_user_data) = (void*)accept_rs->id;
+
+ //start monitor the socket
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id);
+ btsock_thread_add_fd(pth, accept_rs->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD,
+ accept_rs->id);
+ APPL_TRACE_DEBUG("sending connect signal & app fd:%dto app server to accept() the"
+ " connection", accept_rs->app_fd);
+ APPL_TRACE_DEBUG("server fd:%d, scn:%d", sock->our_fd, sock->channel);
+ send_app_connect_signal(sock->our_fd, &accept_rs->addr, sock->channel, 0,
+ accept_rs->app_fd, p_open->tx_mtu);
+ accept_rs->app_fd = -1; //the fd is closed after sent to app
+ }
+}
+
+static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN *p_open, l2cap_socket *sock)
+{
+ bd_copy(sock->addr.address, p_open->rem_bda, 0);
+
+ if (!send_app_psm_or_chan_l(sock)) {
+ APPL_TRACE_ERROR("send_app_psm_or_chan_l failed");
+ return;
+ }
+
+ if (send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, p_open->tx_mtu)) {
+ //start monitoring the socketpair to get call back when app writing data
+ APPL_TRACE_DEBUG("on_l2cap_connect_ind, connect signal sent, slot id:%d, psm:%d,"
+ " server:%d", sock->id, sock->channel, sock->server);
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+ sock->connected = TRUE;
+ }
+ else APPL_TRACE_ERROR("send_app_connect_signal failed");
+}
+
+static void on_cl_l2cap_le_connect_l(tBTA_JV_L2CAP_LE_OPEN *p_open, l2cap_socket *sock)
+{
+ bd_copy(sock->addr.address, p_open->rem_bda, 0);
+
+ if (!send_app_psm_or_chan_l(sock)) {
+ APPL_TRACE_ERROR("send_app_psm_or_chan_l failed");
+ return;
+ }
+
+ if (send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, p_open->tx_mtu)) {
+ //start monitoring the socketpair to get call back when app writing data
+ APPL_TRACE_DEBUG("on_l2cap_connect_ind, connect signal sent, slot id:%d, Chan:%d,"
+ " server:%d", sock->id, sock->channel, sock->server);
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+ sock->connected = TRUE;
+ }
+ else APPL_TRACE_ERROR("send_app_connect_signal failed");
+}
+
+static void on_l2cap_connect(tBTA_JV *p_data, uint32_t id)
+{
+ l2cap_socket *sock;
+ tBTA_JV_L2CAP_OPEN *psm_open = &p_data->l2c_open;
+ tBTA_JV_L2CAP_LE_OPEN *le_open = &p_data->l2c_le_open;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (!sock) {
+ APPL_TRACE_ERROR("on_l2cap_connect on unknown socket");
+ } else {
+ if (sock->fixed_chan && le_open->status == BTA_JV_SUCCESS) {
+ if (!sock->server)
+ on_cl_l2cap_le_connect_l(le_open, sock);
+ else
+ on_srv_l2cap_le_connect_l(le_open, sock);
+ } else if (!sock->fixed_chan && psm_open->status == BTA_JV_SUCCESS) {
+ if (!sock->server)
+ on_cl_l2cap_psm_connect_l(psm_open, sock);
+ else
+ on_srv_l2cap_psm_connect_l(psm_open, sock);
+ }
+ else
+ btsock_l2cap_free_l(sock);
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void on_l2cap_close(tBTA_JV_L2CAP_CLOSE * p_close, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock) {
+ APPL_TRACE_DEBUG("on_l2cap_close, slot id:%d, fd:%d, %s:%d, server:%d",
+ sock->id, sock->our_fd, sock->fixed_chan ? "fixed_chan" : "PSM",
+ sock->channel, sock->server);
+ sock->handle = 0;
+ // TODO: This does not seem to be called...
+ // I'm not sure if this will be called for non-server sockets?
+ if(!sock->fixed_chan && (sock->server == TRUE)) {
+ BTA_JvFreeChannel(sock->channel, BTA_JV_CONN_TYPE_L2CAP);
+ }
+ btsock_l2cap_free_l(sock);
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void on_l2cap_outgoing_congest(tBTA_JV_L2CAP_CONG *p, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock) {
+ sock->outgoing_congest = p->cong ? 1 : 0;
+ //mointer the fd for any outgoing data
+ if (!sock->outgoing_congest) {
+ APPL_TRACE_DEBUG("on_l2cap_outgoing_congest: adding fd to btsock_thread...");
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+
+ }
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void on_l2cap_write_done(void* req_id, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ if (req_id != NULL) {
+ free(req_id); //free the buffer
+ }
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock && !sock->outgoing_congest) {
+ //monitor the fd for any outgoing data
+ APPL_TRACE_DEBUG("on_l2cap_write_done: adding fd to btsock_thread...");
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void on_l2cap_write_fixed_done(void* req_id, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ if (req_id != NULL) {
+ free(req_id); //free the buffer
+ }
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock && !sock->outgoing_congest) {
+ //monitor the fd for any outgoing data
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+
+
+static void on_l2cap_data_ind(tBTA_JV *evt, uint32_t id)
+{
+ l2cap_socket *sock;
+
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ if (sock) {
+ if (sock->fixed_chan) { /* we do these differently */
+
+ tBTA_JV_LE_DATA_IND *p_le_data_ind = &evt->le_data_ind;
+ BT_HDR *p_buf = p_le_data_ind->p_buf;
+ uint8_t *data = (uint8_t*)(p_buf + 1) + p_buf->offset;
+
+ if (packet_put_tail_l(sock, data, p_buf->len))
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_WR, sock->id);
+ else {//connection must be dropped
+ APPL_TRACE_DEBUG("on_l2cap_data_ind() unable to push data to socket - closing"
+ " fixed channel");
+ BTA_JvL2capCloseLE(sock->handle);
+ btsock_l2cap_free_l(sock);
+ }
+
+ } else {
+
+ tBTA_JV_DATA_IND *p_data_ind = &evt->data_ind;
+ UINT8 buffer[L2CAP_MAX_SDU_LENGTH];
+ UINT32 count;
+
+ if (BTA_JvL2capReady(sock->handle, &count) == BTA_JV_SUCCESS) {
+ if (BTA_JvL2capRead(sock->handle, sock->id, buffer, count) == BTA_JV_SUCCESS) {
+ if (packet_put_tail_l(sock, buffer, count))
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_WR,
+ sock->id);
+ else {//connection must be dropped
+ APPL_TRACE_DEBUG("on_l2cap_data_ind() unable to push data to socket"
+ " - closing channel");
+ BTA_JvL2capClose(sock->handle);
+ btsock_l2cap_free_l(sock);
+ }
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+static void btsock_l2cap_cbk(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data)
+{
+ int rc;
+
+ switch (event) {
+ case BTA_JV_L2CAP_START_EVT:
+ on_srv_l2cap_listen_started(&p_data->l2c_start, (uint32_t)user_data);
+ break;
+
+ case BTA_JV_L2CAP_CL_INIT_EVT:
+ on_cl_l2cap_init(&p_data->l2c_cl_init, (uint32_t)user_data);
+ break;
+
+ case BTA_JV_L2CAP_OPEN_EVT:
+ on_l2cap_connect(p_data, (uint32_t)user_data);
+ BTA_JvSetPmProfile(p_data->l2c_open.handle,BTA_JV_PM_ID_1,BTA_JV_CONN_OPEN);
+ break;
+
+ case BTA_JV_L2CAP_CLOSE_EVT:
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_CLOSE_EVT: user_data:%d", (uint32_t)user_data);
+ on_l2cap_close(&p_data->l2c_close, (uint32_t)user_data);
+ break;
+
+ case BTA_JV_L2CAP_DATA_IND_EVT:
+ on_l2cap_data_ind(p_data, (uint32_t)user_data);
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_DATA_IND_EVT");
+ break;
+
+ case BTA_JV_L2CAP_READ_EVT:
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_READ_EVT not used");
+ break;
+
+ case BTA_JV_L2CAP_RECEIVE_EVT:
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_RECEIVE_EVT not used");
+ break;
+
+ case BTA_JV_L2CAP_WRITE_EVT:
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_WRITE_EVT id: %d", (int)user_data);
+ on_l2cap_write_done((void*)p_data->l2c_write.req_id, (uint32_t)user_data);
+ break;
+
+ case BTA_JV_L2CAP_WRITE_FIXED_EVT:
+ APPL_TRACE_DEBUG("BTA_JV_L2CAP_WRITE_FIXED_EVT id: %d", (int)user_data);
+ on_l2cap_write_fixed_done((void*)p_data->l2c_write_fixed.req_id, (uint32_t)user_data);
+ break;
+
+ case BTA_JV_L2CAP_CONG_EVT:
+ on_l2cap_outgoing_congest(&p_data->l2c_cong, (uint32_t)user_data);
+ break;
+
+ default:
+ APPL_TRACE_ERROR("unhandled event %d, slot id:%d", event, (uint32_t)user_data);
+ break;
+ }
+}
+
+/* L2CAP default options for OBEX socket connections */
+const tL2CAP_FCR_OPTS obex_l2c_fcr_opts_def =
+{
+ L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */
+ OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR,/* Tx window size */
+ OBX_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */
+ OBX_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
+ OBX_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
+ OBX_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */
+};
+const tL2CAP_ERTM_INFO obex_l2c_etm_opt =
+{
+ L2CAP_FCR_ERTM_MODE, /* Mandatory for OBEX over l2cap */
+ L2CAP_FCR_CHAN_OPT_ERTM, /* Mandatory for OBEX over l2cap */
+ OBX_USER_RX_POOL_ID,
+ OBX_USER_TX_POOL_ID,
+ OBX_FCR_RX_POOL_ID,
+ OBX_FCR_TX_POOL_ID
+};
+
+/**
+ * When using a dynamic PSM, a PSM allocation is requested from btsock_l2cap_listen_or_connect().
+ * The PSM allocation event is refeived in the JV-callback - currently located in RFC-code -
+ * and this function is called with the newly allocated PSM.
+ */
+void on_l2cap_psm_assigned(int id, int psm) {
+ l2cap_socket *sock;
+ /* Setup ETM settings:
+ * mtu will be set below */
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(id);
+ sock->channel = psm;
+
+ if(btSock_start_l2cap_server_l(sock) != BT_STATUS_SUCCESS) {
+ btsock_l2cap_free_l(sock);
+ }
+
+ pthread_mutex_unlock(&state_lock);
+
+}
+
+static bt_status_t btSock_start_l2cap_server_l(l2cap_socket *sock) {
+ tL2CAP_CFG_INFO cfg;
+ bt_status_t stat = BT_STATUS_SUCCESS;
+ /* Setup ETM settings:
+ * mtu will be set below */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ cfg.fcr_present = TRUE;
+ cfg.fcr = obex_l2c_fcr_opts_def;
+
+ if (sock->fixed_chan) {
+
+ if (BTA_JvL2capStartServerLE(sock->security, 0, NULL, sock->channel,
+ L2CAP_DEFAULT_MTU, NULL, btsock_l2cap_cbk, (void*)sock->id)
+ != BTA_JV_SUCCESS)
+ stat = BT_STATUS_FAIL;
+
+ } else {
+ /* If we have a channel specified in the request, just start the server,
+ * else we request a PSM and start the server after we receive a PSM. */
+ if(sock->channel < 0) {
+ if(BTA_JvGetChannelId(BTA_JV_CONN_TYPE_L2CAP, (void*)sock->id, 0)
+ != BTA_JV_SUCCESS)
+ stat = BT_STATUS_FAIL;
+ } else {
+ if (BTA_JvL2capStartServer(sock->security, 0, &obex_l2c_etm_opt,
+ sock->channel, L2CAP_MAX_SDU_LENGTH, &cfg, btsock_l2cap_cbk, (void*)sock->id)
+ != BTA_JV_SUCCESS)
+ stat = BT_STATUS_FAIL;
+ }
+ }
+ return stat;
+}
+
+static bt_status_t btsock_l2cap_listen_or_connect(const char *name, const bt_bdaddr_t *addr,
+ int channel, int* sock_fd, int flags, char listen)
+{
+ bt_status_t stat;
+ int fixed_chan = 1;
+ l2cap_socket *sock;
+ tL2CAP_CFG_INFO cfg;
+
+ if (!sock_fd)
+ return BT_STATUS_PARM_INVALID;
+
+ if(channel < 0) {
+ // We need to auto assign a PSM
+ fixed_chan = 0;
+ } else {
+ fixed_chan = (channel & L2CAP_MASK_FIXED_CHANNEL) != 0;
+ channel &=~ L2CAP_MASK_FIXED_CHANNEL;
+ }
+
+ if (!is_inited())
+ return BT_STATUS_NOT_READY;
+
+ // TODO: This is kind of bad to lock here, but it is needed for the current design.
+ pthread_mutex_lock(&state_lock);
+
+ sock = btsock_l2cap_alloc_l(name, addr, listen, flags);
+ if (!sock)
+ return BT_STATUS_NOMEM;
+
+ sock->fixed_chan = fixed_chan;
+ sock->channel = channel;
+
+ stat = BT_STATUS_SUCCESS;
+
+ /* Setup ETM settings:
+ * mtu will be set below */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ cfg.fcr_present = TRUE;
+ cfg.fcr = obex_l2c_fcr_opts_def;
+
+ /* "role" is never initialized in rfcomm code */
+ if (listen) {
+ stat = btSock_start_l2cap_server_l(sock);
+ } else {
+ if (fixed_chan) {
+ if (BTA_JvL2capConnectLE(sock->security, 0, NULL, channel,
+ L2CAP_DEFAULT_MTU, NULL, sock->addr.address, btsock_l2cap_cbk,
+ (void*)sock->id) != BTA_JV_SUCCESS)
+ stat = BT_STATUS_FAIL;
+
+ } else {
+ if (BTA_JvL2capConnect(sock->security, 0, &obex_l2c_etm_opt,
+ channel, L2CAP_MAX_SDU_LENGTH, &cfg, sock->addr.address,
+ btsock_l2cap_cbk, (void*)sock->id) != BTA_JV_SUCCESS)
+ stat = BT_STATUS_FAIL;
+ }
+ }
+
+ if (stat == BT_STATUS_SUCCESS) {
+ *sock_fd = sock->app_fd;
+ /* We pass the FD to JAVA, but since it runs in another process, we need to also close
+ * it in native, either straight away, as done when accepting an incoming connection,
+ * or when doing cleanup after this socket */
+ sock->app_fd = -1; /*This leaks the file descriptor. The FD should be closed in
+ JAVA but it apparently do not work */
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION,
+ sock->id);
+ } else {
+ btsock_l2cap_free_l(sock);
+ }
+ pthread_mutex_unlock(&state_lock);
+
+ return stat;
+}
+
+bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags)
+{
+ return btsock_l2cap_listen_or_connect(name, NULL, channel, sock_fd, flags, 1);
+}
+
+bt_status_t btsock_l2cap_connect(const bt_bdaddr_t *bd_addr, int channel, int* sock_fd, int flags)
+{
+ return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags, 0);
+}
+
+/* return TRUE if we have more to send and should wait for user readiness, FALSE else
+ * (for example: unrecoverable error or no data)
+ */
+static BOOLEAN flush_incoming_que_on_wr_signal_l(l2cap_socket *sock)
+{
+ uint8_t *buf;
+ uint32_t len;
+
+ while (packet_get_head_l(sock, &buf, &len)) {
+ int sent = send(sock->our_fd, buf, len, MSG_DONTWAIT);
+
+ if (sent == (signed)len)
+ free(buf);
+ else if (sent >= 0) {
+ packet_put_head_l(sock, buf + sent, len - sent);
+ free(buf);
+ if (!sent) /* special case if other end not keeping up */
+ return TRUE;
+ }
+ else {
+ packet_put_head_l(sock, buf, len);
+ free(buf);
+ return errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN;
+ }
+ }
+
+ return FALSE;
+}
+
+void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id)
+{
+ l2cap_socket *sock;
+ char drop_it = FALSE;
+
+ /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to hold the lock. */
+ pthread_mutex_lock(&state_lock);
+ sock = btsock_l2cap_find_by_id_l(user_id);
+ if (sock) {
+ if ((flags & SOCK_THREAD_FD_RD) && !sock->server) {
+ //app sending data
+ if (sock->connected) {
+ int size = 0;
+
+ if (!(flags & SOCK_THREAD_FD_EXCEPTION) || (ioctl(sock->our_fd, FIONREAD, &size)
+ == 0 && size)) {
+ uint8_t *buffer = malloc(L2CAP_MAX_SDU_LENGTH);
+ //uint8_t *buffer = (uint8_t*)GKI_getbuf(L2CAP_MAX_SDU_LENGTH);
+ /* Apparently we hijack the req_id (UINT32) to pass the pointer to the buffer to
+ * the write complete callback, which call a free... wonder if this works on a
+ * 64 bit platform? */
+ if (buffer != NULL) {
+ /* The socket is created with SOCK_SEQPACKET, hence we read one message at
+ * the time. The maximum size of a message is allocated to ensure data is
+ * not lost. This is okay to do as Android uses virtual memory, hence even
+ * if we only use a fraction of the memory it should not block for others
+ * to use the memory. As the definition of ioctl(FIONREAD) do not clearly
+ * define what value will be returned if multiple messages are written to
+ * the socket before any message is read from the socket, we could
+ * potentially risk to allocate way more memory than needed. One of the use
+ * cases for this socket is obex where multiple 64kbyte messages are
+ * typically written to the socket in a tight loop, hence we risk the ioctl
+ * will return the total amount of data in the buffer, which could be
+ * multiple 64kbyte chunks.
+ * UPDATE: As bluedroid cannot handle 64kbyte buffers, the size is reduced
+ * to around 8kbyte - and using malloc for buffer allocation here seems to
+ * be wrong
+ * UPDATE: Since we are responsible for freeing the buffer in the
+ * write_complete_ind, it is OK to use malloc. */
+
+ int count = recv(fd, buffer, L2CAP_MAX_SDU_LENGTH,
+ MSG_NOSIGNAL | MSG_DONTWAIT);
+ APPL_TRACE_DEBUG("btsock_l2cap_signaled - %d bytes received from socket",
+ count);
+ if (sock->fixed_chan) {
+ if(BTA_JvL2capWriteFixed(sock->channel, (BD_ADDR*)&sock->addr,
+ (UINT32)buffer, btsock_l2cap_cbk, buffer, count,
+ (void *)user_id) != BTA_JV_SUCCESS) {
+ // On fail, free the buffer
+ on_l2cap_write_fixed_done(buffer, user_id);
+ }
+ } else {
+ if(BTA_JvL2capWrite(sock->handle, (UINT32)buffer, buffer, count,
+ (void *)user_id) != BTA_JV_SUCCESS) {
+ // On fail, free the buffer
+ on_l2cap_write_done(buffer, user_id);
+ }
+ }
+ } else {
+ // This cannot happen.
+ APPL_TRACE_ERROR("Unable to allocate memory for data packet from JAVA...")
+ }
+ }
+ } else
+ drop_it = TRUE;
+ }
+ if (flags & SOCK_THREAD_FD_WR) {
+ //app is ready to receive more data, tell stack to enable the data flow
+ if (flush_incoming_que_on_wr_signal_l(sock) && sock->connected)
+ btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_WR, sock->id);
+ }
+ if (drop_it || (flags & SOCK_THREAD_FD_EXCEPTION)) {
+ int size = 0;
+ if (drop_it || ioctl(sock->our_fd, FIONREAD, &size) != 0 || size == 0)
+ btsock_l2cap_free_l(sock);
+ }
+ }
+ pthread_mutex_unlock(&state_lock);
+}
+
+
+
diff --git a/btif/src/btif_sock_rfc.c b/btif/src/btif_sock_rfc.c
index 0ece691c5..01326ab48 100644
--- a/btif/src/btif_sock_rfc.c
+++ b/btif/src/btif_sock_rfc.c
@@ -36,6 +36,11 @@
#include "btif_sock_sdp.h"
#include "btif_sock_thread.h"
#include "btif_sock_util.h"
+/* The JV interface can have only one user, hence we need to call a few
+ * L2CAP functions from this file. */
+#include "btif_sock_l2cap.h"
+
+
#include "btif_util.h"
#include "btm_api.h"
#include "btm_int.h"
@@ -66,7 +71,9 @@ typedef struct {
uint32_t id; // Non-zero indicates a valid (in-use) slot.
int security;
int scn; // Server channel number
+ int scn_notified;
bt_bdaddr_t addr;
+ int is_service_uuid_valid;
uint8_t service_uuid[16];
char service_name[256];
int fd;
@@ -93,7 +100,7 @@ static rfc_slot_t *find_free_slot(void);
static void cleanup_rfc_slot(rfc_slot_t *rs);
static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data);
static void *rfcomm_cback(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data);
-static bool send_app_scn(const rfc_slot_t *rs);
+static bool send_app_scn(rfc_slot_t *rs);
static bool is_init_done(void) {
return pth != -1;
@@ -194,14 +201,18 @@ static rfc_slot_t *alloc_rfc_slot(const bt_bdaddr_t *addr, const char *name, con
slot->security = security;
slot->scn = channel;
- if (uuid)
+ if(!is_uuid_empty(uuid)) {
memcpy(slot->service_uuid, uuid, sizeof(slot->service_uuid));
- else
- memset(&slot->service_uuid, 0, sizeof(slot->service_uuid));
-
- if (name)
+ slot->is_service_uuid_valid = true;
+ } else {
+ memset(slot->service_uuid, 0, sizeof(slot->service_uuid));
+ slot->is_service_uuid_valid = false;
+ }
+ if(name && *name) {
strlcpy(slot->service_name, name, sizeof(slot->service_name));
-
+ } else {
+ memset(slot->service_name, 0, sizeof(slot->service_name));
+ }
if (addr)
slot->addr = *addr;
@@ -241,7 +252,9 @@ static rfc_slot_t *create_srv_accept_rfc_slot(rfc_slot_t *srv_rs, const bt_bdadd
bt_status_t btsock_rfc_listen(const char *service_name, const uint8_t *service_uuid, int channel, int *sock_fd, int flags) {
assert(sock_fd != NULL);
- assert(service_uuid != NULL || (channel >= 1 && channel <= MAX_RFC_CHANNEL));
+ assert((service_uuid != NULL)
+ || (channel >= 1 && channel <= MAX_RFC_CHANNEL)
+ || ((flags & BTSOCK_FLAG_NO_SDP) != 0));
*sock_fd = INVALID_FD;
@@ -250,13 +263,18 @@ bt_status_t btsock_rfc_listen(const char *service_name, const uint8_t *service_u
if (!is_init_done())
return BT_STATUS_NOT_READY;
- if (!is_uuid_empty(service_uuid)) {
- // Use a pre-defined channel # if the UUID is reserved.
- int reserved_channel = get_reserved_rfc_channel(service_uuid);
- if (reserved_channel != -1)
- channel = reserved_channel;
- } else {
- service_uuid = UUID_SPP; // Use serial port profile to listen to specified channel
+ if((flags & BTSOCK_FLAG_NO_SDP) == 0) {
+ if(is_uuid_empty(service_uuid)) {
+ APPL_TRACE_DEBUG("BTA_JvGetChannelId: service_uuid not set AND "
+ "BTSOCK_FLAG_NO_SDP is not set - changing to SPP");
+ service_uuid = UUID_SPP; // Use serial port profile to listen to specified channel
+ } else {
+ //Check the service_uuid. overwrite the channel # if reserved
+ int reserved_channel = get_reserved_rfc_channel(service_uuid);
+ if (reserved_channel > 0) {
+ channel = reserved_channel;
+ }
+ }
}
int status = BT_STATUS_FAIL;
@@ -267,9 +285,16 @@ bt_status_t btsock_rfc_listen(const char *service_name, const uint8_t *service_u
LOG_ERROR("%s unable to allocate RFCOMM slot.", __func__);
goto out;
}
-
- BTA_JvCreateRecordByUser((void *)(uintptr_t)slot->id);
+ APPL_TRACE_DEBUG("BTA_JvGetChannelId: service_name: %s - channel: %d", service_name, channel);
+ BTA_JvGetChannelId(BTA_JV_CONN_TYPE_RFCOMM, (void*) slot->id, channel);
*sock_fd = slot->app_fd; // Transfer ownership of fd to caller.
+ /*TODO:
+ * We are leaking one of the app_fd's - either the listen socket, or the connection socket.
+ * WE need to close this in native, as the FD might belong to another process
+ - This is the server socket FD
+ - For accepted connections, we close the FD after passing it to JAVA.
+ - Try to simply remove the = -1 to free the FD at rs cleanup.*/
+// close(rs->app_fd);
slot->app_fd = INVALID_FD; // Drop our reference to the fd.
btsock_thread_add_fd(pth, slot->fd, BTSOCK_RFCOMM, SOCK_THREAD_FD_EXCEPTION, slot->id);
@@ -339,16 +364,9 @@ out:;
}
static int create_server_sdp_record(rfc_slot_t *slot) {
- if (slot->scn > 0) {
- if (!BTM_TryAllocateSCN(slot->scn)) {
- LOG_ERROR("%s attempting to allocate fixed channel %d which is already in use.", __func__, slot->scn);
- return false;
+ if(slot->scn == 0) {
+ return false;
}
- } else if ((slot->scn = BTM_AllocateSCN()) == 0) {
- LOG_ERROR("%s unable to allocate RFCOMM server channel.", __func__);
- return false;
- }
-
slot->sdp_handle = add_rfc_sdp_rec(slot->service_name, slot->service_uuid, slot->scn);
return (slot->sdp_handle > 0);
}
@@ -395,9 +413,15 @@ static void cleanup_rfc_slot(rfc_slot_t *slot) {
slot->rfc_port_handle = 0;
memset(&slot->f, 0, sizeof(slot->f));
slot->id = 0;
+ slot->scn_notified = false;
}
-static bool send_app_scn(const rfc_slot_t *slot) {
+static bool send_app_scn(rfc_slot_t *slot) {
+ if(slot->scn_notified == true) {
+ //already send, just return success.
+ return true;
+ }
+ slot->scn_notified = true;
return sock_send_all(slot->fd, (const uint8_t*)&slot->scn, sizeof(slot->scn)) == sizeof(slot->scn);
}
@@ -407,7 +431,8 @@ static bool send_app_connect_signal(int fd, const bt_bdaddr_t* addr, int channel
cs.bd_addr = *addr;
cs.channel = channel;
cs.status = status;
-
+ cs.max_rx_packet_size = 0; // not used for RFCOMM
+ cs.max_tx_packet_size = 0; // not used for RFCOMM
if (send_fd == INVALID_FD)
return sock_send_all(fd, (const uint8_t *)&cs, sizeof(cs)) == sizeof(cs);
@@ -439,11 +464,6 @@ static void on_srv_rfc_listen_started(tBTA_JV_RFCOMM_START *p_start, uint32_t id
if (p_start->status == BTA_JV_SUCCESS) {
slot->rfc_handle = p_start->handle;
-
- if (!send_app_scn(slot)) {
- LOG_ERROR("%s unable to send server channel number for slot %d.", __func__, slot->id);
- cleanup_rfc_slot(slot);
- }
} else
cleanup_rfc_slot(slot);
@@ -583,6 +603,52 @@ static void *rfcomm_cback(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) {
static void jv_dm_cback(tBTA_JV_EVT event, tBTA_JV *p_data, void *user_data) {
uint32_t id = (uintptr_t)user_data;
switch(event) {
+ case BTA_JV_GET_SCN_EVT:
+ {
+ pthread_mutex_lock(&slot_lock);
+ rfc_slot_t* rs = find_rfc_slot_by_id(id);
+ int new_scn = p_data->scn;
+
+ if(rs && (new_scn != 0))
+ {
+ rs->scn = new_scn;
+ /* BTA_JvCreateRecordByUser will only create a record if a UUID is specified,
+ * else it just allocate a RFC channel and start the RFCOMM thread - needed
+ * for the java
+ * layer to get a RFCOMM channel.
+ * If uuid is null the create_sdp_record() will be called from Java when it
+ * has received the RFCOMM and L2CAP channel numbers through the sockets.*/
+
+ // Send channel ID to java layer
+ if(!send_app_scn(rs)){
+ //closed
+ APPL_TRACE_DEBUG("send_app_scn() failed, close rs->id:%d", rs->id);
+ cleanup_rfc_slot(rs);
+ } else {
+ if(rs->is_service_uuid_valid == true) {
+ // We already have data for SDP record, create it (RFC-only profiles)
+ BTA_JvCreateRecordByUser((void *)rs->id);
+ } else {
+ APPL_TRACE_DEBUG("is_service_uuid_valid==false - don't set SDP-record, "
+ "just start the RFCOMM server", rs->id);
+ //now start the rfcomm server after sdp & channel # assigned
+ BTA_JvRfcommStartServer(rs->security, rs->role, rs->scn, MAX_RFC_SESSION,
+ rfcomm_cback, (void*)rs->id);
+ }
+ }
+ } else if(rs) {
+ APPL_TRACE_ERROR("jv_dm_cback: Error: allocate channel %d, slot found:%p", rs->scn, rs);
+ cleanup_rfc_slot(rs);
+ }
+ pthread_mutex_unlock(&slot_lock);
+ break;
+ }
+ case BTA_JV_GET_PSM_EVT:
+ {
+ APPL_TRACE_DEBUG("Received PSM: 0x%04x", p_data->psm);
+ on_l2cap_psm_assigned(id, p_data->psm);
+ break;
+ }
case BTA_JV_CREATE_RECORD_EVT: {
pthread_mutex_lock(&slot_lock);
@@ -717,6 +783,9 @@ void btsock_rfc_signaled(UNUSED_ATTR int fd, int flags, uint32_t user_id) {
// Make sure there's data pending in case the peer closed the socket.
int size = 0;
if (!(flags & SOCK_THREAD_FD_EXCEPTION) || (ioctl(slot->fd, FIONREAD, &size) == 0 && size))
+ //unlock before BTA_JvRfcommWrite to avoid deadlock on concurrnet multi rfcomm connectoins
+ //concurrnet multi rfcomm connectoins
+ pthread_mutex_unlock(&slot_lock);
BTA_JvRfcommWrite(slot->rfc_handle, slot->id);
} else {
LOG_ERROR("%s socket signaled for read while disconnected, slot: %d, channel: %d", __func__, slot->id, slot->scn);
diff --git a/btif/src/btif_sock_sdp.c b/btif/src/btif_sock_sdp.c
index 1fcae8f4f..375a3d3f3 100644
--- a/btif/src/btif_sock_sdp.c
+++ b/btif/src/btif_sock_sdp.c
@@ -29,6 +29,7 @@
#include "btif_common.h"
#include "btif_util.h"
+#include "btif_sock_util.h"
#include "bta_api.h"
#include "bt_target.h"
#include "gki.h"
@@ -91,35 +92,7 @@ static const tBTA_OP_FMT bta_ops_obj_fmt[OBEX_PUSH_NUM_FORMATS] = {
| BTA_OP_ANY_MASK)
#endif
-/*
- * This is horrible design - to reserve channel ID's and use them to magically
- * link a channel number to a hard coded SDP entry.
- *
- * TODO: expose a prober SDP API, to avoid hacks like this, and make it possible
- * to set useful names for the ServiceName
- */
-#define BTA_MAP_MSG_TYPE_EMAIL 0x01
-#define BTA_MAP_MSG_TYPE_SMS_GSM 0x02
-#define BTA_MAP_MSG_TYPE_SMS_CDMA 0x04
-#define BTA_MAP_MSG_TYPE_MMS 0x08
-
-#define BTA_MAP_MAS_ID_SMSMMS 0
-#define BTA_MAP_MAS_ID_EMAIL 1
-
-// MAP 1.1
-#define BTA_MAP_DEFAULT_VERSION 0x0101
-
-typedef struct {
- uint8_t mas_id; // the MAS instance id
- const char *service_name; // Description of the MAS instance
- uint8_t supported_message_types; // Server supported message types - SMS/MMS/EMAIL
-} tBTA_MAP_CFG;
-
-const tBTA_MAP_CFG bta_map_cfg_sms = {
- BTA_MAP_MAS_ID_SMSMMS,
- "MAP SMS",
- BTA_MAP_MSG_TYPE_SMS_GSM | BTA_MAP_MSG_TYPE_SMS_CDMA
-};
+
#define RESERVED_SCN_PBS 19
#define RESERVED_SCN_OPS 12
@@ -289,67 +262,6 @@ error:
"service_name: %s", stage, name);
return 0;
}
-
-// Registers a service with the given |name| and |channel| in the SDP database
-// as a MAP protocol.
-static int add_maps_sdp(const char *name, const int channel) {
- APPL_TRACE_DEBUG("add_maps_sdp: scn %d, service_name %s", channel,
- name);
-
- uint32_t handle = SDP_CreateRecord();
- if (handle == 0) {
- APPL_TRACE_ERROR("add_maps_sdp: failed to create sdp record, "
- "service_name: %s", name);
- return 0;
- }
-
- // Create the base SDP record.
- char *stage = "create_base_record";
- if (!create_base_record(handle, name, channel, TRUE /* with_obex */))
- goto error;
-
- // add service class
- uint16_t service = UUID_SERVCLASS_MESSAGE_ACCESS;
- stage = "service_class";
- if (!SDP_AddServiceClassIdList(handle, 1, &service))
- goto error;
-
- // TODO: To add support for EMAIL set below depending on the scn to either
- // SMS or Email
- const tBTA_MAP_CFG *bta_map_cfg = &bta_map_cfg_sms;
-
- // Add in the Bluetooth Profile Descriptor List
- stage = "profile_descriptor_list";
- if (!SDP_AddProfileDescriptorList(handle,
- UUID_SERVCLASS_MAP_PROFILE,
- BTA_MAP_DEFAULT_VERSION))
- goto error;
-
- stage = "mas_instance_id";
- if (!SDP_AddAttribute(handle, ATTR_ID_MAS_INSTANCE_ID, UINT_DESC_TYPE,
- (uint32_t)1, (uint8_t*)(&bta_map_cfg->mas_id)))
- goto error;
-
- stage = "support_message_types";
- if (!SDP_AddAttribute(handle, ATTR_ID_SUPPORTED_MSG_TYPE, UINT_DESC_TYPE,
- (uint32_t)1,
- (uint8_t*)(&bta_map_cfg->supported_message_types)))
- goto error;
-
- // Notify the system that we've got a new service class UUID.
- bta_sys_add_uuid(service); // UUID_SERVCLASS_MESSAGE_ACCESS
- APPL_TRACE_DEBUG("ad_maps_sdp: service registered successfully, "
- "service_name: %s, handle 0x%08x)", name, handle);
-
- return handle;
-
-error:
- SDP_DeleteRecord(handle);
- APPL_TRACE_ERROR("add_maps_sdp: failed to register MAP service, stage: %s, "
- "service_name: %s", stage, name);
- return 0;
-}
-
// Registers a service with the given |name| and |channel| as an OBEX Push
// protocol.
static int add_ops_sdp(const char *name, const int channel) {
@@ -489,9 +401,9 @@ static int add_rfc_sdp_by_uuid(const char *name, const uint8_t *uuid,
handle = add_pbap_sdp(name, final_channel);
} else if (UUID_MATCHES(UUID_SPP, uuid)) {
handle = add_spp_sdp(name, final_channel);
- } else if (UUID_MATCHES(UUID_MAPS_MAS,uuid)) {
- // MAP Server is always channel 19
- handle = add_maps_sdp(name, final_channel);
+ } else if (UUID_MATCHES(UUID_MAP_MAS,uuid)) {
+ // Record created by new SDP create record interface
+ handle = 0xff;
} else {
handle = add_sdp_by_uuid(name, uuid, final_channel);
}
diff --git a/btif/src/btif_sock_thread.c b/btif/src/btif_sock_thread.c
index 810601383..18d5fa0d0 100644
--- a/btif/src/btif_sock_thread.c
+++ b/btif/src/btif_sock_thread.c
@@ -176,9 +176,26 @@ static inline int create_thread(void *(*start_routine)(void *), void * arg,
pthread_attr_t thread_attr;
pthread_attr_init(&thread_attr);
pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
- return pthread_create(thread_id, &thread_attr, start_routine, arg);
-}
+ int policy;
+ int min_pri=0;
+ int ret = -1;
+ struct sched_param param;
+ if ((ret = pthread_create(thread_id, &thread_attr, start_routine, arg))!=0 )
+ {
+ APPL_TRACE_ERROR("pthread_create : %s", strerror(errno));
+ return ret;
+ }
+ /* We need to lower the priority of this thread to ensure the stack gets
+ * priority over transfer to a socket */
+ pthread_getschedparam(*thread_id, &policy, &param);
+ min_pri = sched_get_priority_min(policy);
+ if (param.sched_priority > min_pri) {
+ param.sched_priority -= 1;
+ }
+ pthread_setschedparam(*thread_id, policy, &param);
+ return ret;
+}
static void init_poll(int cmd_fd);
static int alloc_thread_slot()
{
diff --git a/btif/src/btif_sock_util.c b/btif/src/btif_sock_util.c
index ee49eeba5..baa3ed3d9 100644
--- a/btif/src/btif_sock_util.c
+++ b/btif/src/btif_sock_util.c
@@ -157,6 +157,8 @@ int sock_send_fd(int sock_fd, const uint8_t* buf, int len, int send_fd)
memset(&msg, 0, sizeof(msg));
}
BTIF_TRACE_DEBUG("close fd:%d after sent", send_fd);
+ // TODO: This seems wrong - if the FD is not opened in JAVA before this is called
+ // we get a "socket closed" exception in java, when reading from the socket...
close(send_fd);
return ret_len;
}
diff --git a/conf/bt_stack.conf b/conf/bt_stack.conf
index 711fe519b..b1f848e34 100644
--- a/conf/bt_stack.conf
+++ b/conf/bt_stack.conf
@@ -36,3 +36,4 @@ TRC_GATT=2
TRC_SMP=2
TRC_BTAPP=2
TRC_BTIF=2
+TRC_GAP=2
diff --git a/include/bt_target.h b/include/bt_target.h
index 82f487743..bf49ac30a 100644
--- a/include/bt_target.h
+++ b/include/bt_target.h
@@ -257,6 +257,13 @@
#define BTM_CMD_POOL_ID GKI_POOL_ID_2
#endif
+#ifndef OBX_LRG_DATA_POOL_SIZE
+#define OBX_LRG_DATA_POOL_SIZE GKI_BUF4_SIZE
+#endif
+
+#ifndef OBX_LRG_DATA_POOL_ID
+#define OBX_LRG_DATA_POOL_ID GKI_POOL_ID_4
+#endif
/* Used to send data to L2CAP. */
#ifndef GAP_DATA_POOL_ID
#define GAP_DATA_POOL_ID GKI_POOL_ID_3
@@ -699,7 +706,7 @@
/* Used for features using fixed channels; set to zero if no fixed channels supported (BLE, etc.) */
/* Excluding L2CAP signaling channel and UCD */
#ifndef L2CAP_NUM_FIXED_CHNLS
-#define L2CAP_NUM_FIXED_CHNLS 4
+#define L2CAP_NUM_FIXED_CHNLS 32
#endif
/* First fixed channel supported */
@@ -731,6 +738,15 @@
#define L2CAP_CONFORMANCE_TESTING FALSE
#endif
+/*
+ * Max bytes per connection to buffer locally before dropping the
+ * connection if local client does not receive it - default is 1MB
+ */
+#ifndef L2CAP_MAX_RX_BUFFER
+#define L2CAP_MAX_RX_BUFFER 0x100000
+#endif
+
+
#ifndef TIMER_PARAM_TYPE
#define TIMER_PARAM_TYPE UINT32
#endif
@@ -906,7 +922,7 @@
/* The maximum number of SDP records the server can support. */
#ifndef SDP_MAX_RECORDS
-#define SDP_MAX_RECORDS 20
+#define SDP_MAX_RECORDS 30
#endif
/* The maximum number of attributes in each record. */
@@ -1075,6 +1091,93 @@
/******************************************************************************
**
+** OBEX
+**
+******************************************************************************/
+#define OBX_14_INCLUDED FALSE
+/* This option is application when OBX_14_INCLUDED=TRUE
+ Pool ID where to reassemble the SDU.
+ This Pool will allow buffers to be used that are larger than
+ the L2CAP_MAX_MTU. */
+#ifndef OBX_USER_RX_POOL_ID
+#define OBX_USER_RX_POOL_ID OBX_LRG_DATA_POOL_ID
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+ Pool ID where to hold the SDU.
+ This Pool will allow buffers to be used that are larger than
+ the L2CAP_MAX_MTU. */
+#ifndef OBX_USER_TX_POOL_ID
+#define OBX_USER_TX_POOL_ID OBX_LRG_DATA_POOL_ID
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+GKI Buffer Pool ID used to hold MPS segments during SDU reassembly
+*/
+#ifndef OBX_FCR_RX_POOL_ID
+#define OBX_FCR_RX_POOL_ID HCI_ACL_POOL_ID
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+GKI Buffer Pool ID used to hold MPS segments used in (re)transmissions.
+L2CAP_DEFAULT_ERM_POOL_ID is specified to use the HCI ACL data pool.
+Note: This pool needs to have enough buffers to hold two times the window size negotiated
+ in the L2CA_SetFCROptions (2 * tx_win_size) to allow for retransmissions.
+ The size of each buffer must be able to hold the maximum MPS segment size passed in
+ L2CA_SetFCROptions plus BT_HDR (8) + HCI preamble (4) + L2CAP_MIN_OFFSET (11 - as of BT 2.1 + EDR Spec).
+*/
+#ifndef OBX_FCR_TX_POOL_ID
+#define OBX_FCR_TX_POOL_ID HCI_ACL_POOL_ID
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+Size of the transmission window when using enhanced retransmission mode. Not used
+in basic and streaming modes. Range: 1 - 63
+*/
+#ifndef OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR
+#define OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR 20
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+Number of transmission attempts for a single I-Frame before taking
+Down the connection. Used In ERTM mode only. Value is Ignored in basic and
+Streaming modes.
+Range: 0, 1-0xFF
+0 - infinite retransmissions
+1 - single transmission
+*/
+#ifndef OBX_FCR_OPT_MAX_TX_B4_DISCNT
+#define OBX_FCR_OPT_MAX_TX_B4_DISCNT 20
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+Retransmission Timeout
+Range: Minimum 2000 (2 secs) on BR/EDR when supporting PBF.
+ */
+#ifndef OBX_FCR_OPT_RETX_TOUT
+#define OBX_FCR_OPT_RETX_TOUT 2000
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+Monitor Timeout
+Range: Minimum 12000 (12 secs) on BR/EDR when supporting PBF.
+*/
+#ifndef OBX_FCR_OPT_MONITOR_TOUT
+#define OBX_FCR_OPT_MONITOR_TOUT 12000
+#endif
+
+/* This option is application when OBX_14_INCLUDED=TRUE
+Maximum PDU payload size.
+Suggestion: The maximum amount of data that will fit into a 3-DH5 packet.
+Range: 2 octets
+*/
+#ifndef OBX_FCR_OPT_MAX_PDU_SIZE
+#define OBX_FCR_OPT_MAX_PDU_SIZE L2CAP_MPS_OVER_BR_EDR
+#endif
+
+
+/******************************************************************************
+**
** BNEP
**
******************************************************************************/
@@ -1280,6 +1383,25 @@
**
******************************************************************************/
+#ifndef GAP_INCLUDED
+#define GAP_INCLUDED TRUE
+#endif
+
+/* This is set to enable use of GAP L2CAP connections. */
+#ifndef GAP_CONN_INCLUDED
+#define GAP_CONN_INCLUDED TRUE
+#endif
+
+/* This is set to enable posting event for data write */
+#ifndef GAP_CONN_POST_EVT_INCLUDED
+#define GAP_CONN_POST_EVT_INCLUDED FALSE
+#endif
+
+/* The maximum number of simultaneous GAP L2CAP connections. */
+#ifndef GAP_MAX_CONNECTIONS
+#define GAP_MAX_CONNECTIONS 30
+#endif
+
/* keep the raw data received from SDP server in database. */
#ifndef SDP_RAW_DATA_INCLUDED
#define SDP_RAW_DATA_INCLUDED TRUE
diff --git a/main/Android.mk b/main/Android.mk
index ffb2fefaa..d0c4956a4 100644
--- a/main/Android.mk
+++ b/main/Android.mk
@@ -35,7 +35,7 @@ LOCAL_SRC_FILES += \
../btif/src/btif_hf_client.c \
../btif/src/btif_hh.c \
../btif/src/btif_hl.c \
- ../btif/src/btif_mce.c \
+ ../btif/src/btif_sdp.c \
../btif/src/btif_media_task.c \
../btif/src/btif_pan.c \
../btif/src/btif_profile_queue.c \
@@ -43,9 +43,11 @@ LOCAL_SRC_FILES += \
../btif/src/btif_sm.c \
../btif/src/btif_sock.c \
../btif/src/btif_sock_rfc.c \
+ ../btif/src/btif_sock_l2cap.c \
../btif/src/btif_sock_sco.c \
../btif/src/btif_sock_sdp.c \
../btif/src/btif_sock_thread.c \
+ ../btif/src/btif_sdp_server.c \
../btif/src/btif_sock_util.c \
../btif/src/btif_storage.c \
../btif/src/btif_util.c \
diff --git a/stack/Android.mk b/stack/Android.mk
index 9a48f62d4..22f013f82 100644
--- a/stack/Android.mk
+++ b/stack/Android.mk
@@ -153,6 +153,8 @@ LOCAL_SRC_FILES:= \
./l2cap/l2cap_client.c \
./gap/gap_api.c \
./gap/gap_ble.c \
+ ./gap/gap_conn.c \
+ ./gap/gap_utils.c \
../vnd/ble/vendor_ble.c
LOCAL_MODULE := libbt-brcm_stack
diff --git a/stack/gap/gap_conn.c b/stack/gap/gap_conn.c
new file mode 100644
index 000000000..67b89225a
--- /dev/null
+++ b/stack/gap/gap_conn.c
@@ -0,0 +1,1284 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2013 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.
+ *
+ ******************************************************************************/
+
+
+#include "bt_target.h"
+#include "bt_utils.h"
+#include "btu.h"
+#include "gap_int.h"
+#include "l2cdefs.h"
+#include "l2c_int.h"
+#include <string.h>
+#if GAP_CONN_INCLUDED == TRUE
+#include "btm_int.h"
+
+/********************************************************************************/
+/* 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 gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id);
+static void gap_connect_cfm (UINT16 l2cap_cid, UINT16 result);
+static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
+static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
+static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested);
+
+static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid);
+static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle);
+static tGAP_CCB *gap_allocate_ccb (void);
+static void gap_release_ccb (tGAP_CCB *p_ccb);
+
+/*******************************************************************************
+**
+** Function gap_conn_init
+**
+** Description This function is called to initialize GAP connection management
+**
+** Returns void
+**
+*******************************************************************************/
+void gap_conn_init (void)
+{
+#if AMP_INCLUDED == TRUE
+ gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind;
+ gap_cb.conn.reg_info.pAMP_ConnectCfm_Cb = gap_connect_cfm;
+ gap_cb.conn.reg_info.pAMP_ConnectPnd_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_ConfigInd_Cb = gap_config_ind;
+ gap_cb.conn.reg_info.pAMP_ConfigCfm_Cb = gap_config_cfm;
+ gap_cb.conn.reg_info.pAMP_DisconnectInd_Cb = gap_disconnect_ind;
+ gap_cb.conn.reg_info.pAMP_DisconnectCfm_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_QoSViolationInd_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_DataInd_Cb = gap_data_ind;
+ gap_cb.conn.reg_info.pAMP_CongestionStatus_Cb = gap_congestion_ind;
+ gap_cb.conn.reg_info.pAMP_TxComplete_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_MoveInd_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_MoveRsp_Cb = NULL;
+ gap_cb.conn.reg_info.pAMP_MoveCfm_Cb = NULL; //gap_move_cfm
+ gap_cb.conn.reg_info.pAMP_MoveCfmRsp_Cb = NULL; //gap_move_cfm_rsp
+
+#else
+ gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind;
+ gap_cb.conn.reg_info.pL2CA_ConnectCfm_Cb = gap_connect_cfm;
+ gap_cb.conn.reg_info.pL2CA_ConnectPnd_Cb = NULL;
+ gap_cb.conn.reg_info.pL2CA_ConfigInd_Cb = gap_config_ind;
+ gap_cb.conn.reg_info.pL2CA_ConfigCfm_Cb = gap_config_cfm;
+ gap_cb.conn.reg_info.pL2CA_DisconnectInd_Cb = gap_disconnect_ind;
+ gap_cb.conn.reg_info.pL2CA_DisconnectCfm_Cb = NULL;
+ gap_cb.conn.reg_info.pL2CA_QoSViolationInd_Cb = NULL;
+ gap_cb.conn.reg_info.pL2CA_DataInd_Cb = gap_data_ind;
+ gap_cb.conn.reg_info.pL2CA_CongestionStatus_Cb = gap_congestion_ind;
+ gap_cb.conn.reg_info.pL2CA_TxComplete_Cb = NULL;
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnOpen
+**
+** Description This function is called to open an L2CAP connection.
+**
+** Parameters: is_server - If TRUE, the connection is not created
+** but put into a "listen" mode waiting for
+** the remote side to connect.
+**
+** service_id - Unique service ID from
+** BTM_SEC_SERVICE_FIRST_EMPTY (6)
+** to BTM_SEC_MAX_SERVICE_RECORDS (32)
+**
+** p_rem_bda - Pointer to remote BD Address.
+** If a server, and we don't care about the
+** remote BD Address, then NULL should be passed.
+**
+** psm - the PSM used for the connection
+**
+** p_config - Optional pointer to configuration structure.
+** If NULL, the default GAP configuration will
+** be used.
+**
+** security - security flags
+** chan_mode_mask - (GAP_FCR_CHAN_OPT_BASIC, GAP_FCR_CHAN_OPT_ERTM,
+** GAP_FCR_CHAN_OPT_STREAM)
+**
+** p_cb - Pointer to callback function for events.
+**
+** Returns handle of the connection if successful, else GAP_INVALID_HANDLE
+**
+*******************************************************************************/
+UINT16 GAP_ConnOpen (char *p_serv_name, UINT8 service_id, BOOLEAN is_server,
+ BD_ADDR p_rem_bda, UINT16 psm, tL2CAP_CFG_INFO *p_cfg,
+ tL2CAP_ERTM_INFO *ertm_info, UINT16 security, UINT8 chan_mode_mask,
+ tGAP_CONN_CALLBACK *p_cb)
+{
+ tGAP_CCB *p_ccb;
+ UINT16 cid;
+ tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}};
+
+ GAP_TRACE_EVENT ("GAP_CONN - Open Request");
+
+ /* Allocate a new CCB. Return if none available. */
+ if ((p_ccb = gap_allocate_ccb()) == NULL)
+ return (GAP_INVALID_HANDLE);
+
+ /* If caller specified a BD address, save it */
+ if (p_rem_bda)
+ {
+ /* the bd addr is not BT_BD_ANY, then a bd address was specified */
+ if (memcmp (p_rem_bda, BT_BD_ANY, BD_ADDR_LEN))
+ p_ccb->rem_addr_specified = TRUE;
+
+ memcpy (&p_ccb->rem_dev_address[0], p_rem_bda, BD_ADDR_LEN);
+ }
+ else if (!is_server)
+ {
+ /* remore addr is not specified and is not a server -> bad */
+ return (GAP_INVALID_HANDLE);
+ }
+
+ /* A client MUST have specified a bd addr to connect with */
+ if (!p_ccb->rem_addr_specified && !is_server)
+ {
+ gap_release_ccb (p_ccb);
+ GAP_TRACE_ERROR ("GAP ERROR: Client must specify a remote BD ADDR to connect to!");
+ return (GAP_INVALID_HANDLE);
+ }
+
+ /* Check if configuration was specified */
+ if (p_cfg)
+ p_ccb->cfg = *p_cfg;
+
+ p_ccb->p_callback = p_cb;
+
+ /* If originator, use a dynamic PSM */
+#if AMP_INCLUDED == TRUE
+ if (!is_server)
+ gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = NULL;
+ else
+ gap_cb.conn.reg_info.pAMP_ConnectInd_Cb = gap_connect_ind;
+#else
+ if (!is_server)
+ gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = NULL;
+ else
+ gap_cb.conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind;
+#endif
+
+ /* Register the PSM with L2CAP */
+ if ((p_ccb->psm = L2CA_REGISTER (psm, &gap_cb.conn.reg_info,
+ AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE)) == 0)
+ {
+ GAP_TRACE_ERROR ("GAP_ConnOpen: Failure registering PSM 0x%04x", psm);
+ gap_release_ccb (p_ccb);
+ return (GAP_INVALID_HANDLE);
+ }
+
+ /* Register with Security Manager for the specific security level */
+ p_ccb->service_id = service_id;
+ if (!BTM_SetSecurityLevel ((UINT8)!is_server, p_serv_name,
+ p_ccb->service_id, security, p_ccb->psm, 0, 0))
+ {
+ GAP_TRACE_ERROR ("GAP_CONN - Security Error");
+ gap_release_ccb (p_ccb);
+ return (GAP_INVALID_HANDLE);
+ }
+
+ /* Fill in eL2CAP parameter data */
+ if( p_ccb->cfg.fcr_present )
+ {
+ if(ertm_info == NULL) {
+ p_ccb->ertm_info.preferred_mode = p_ccb->cfg.fcr.mode;
+ p_ccb->ertm_info.user_rx_pool_id = GAP_DATA_POOL_ID;
+ p_ccb->ertm_info.user_tx_pool_id = GAP_DATA_POOL_ID;
+ p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_DEFAULT_ERM_POOL_ID;
+ p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_DEFAULT_ERM_POOL_ID;
+ } else {
+ p_ccb->ertm_info = *ertm_info;
+ }
+ }
+
+ /* optional FCR channel modes */
+ if(ertm_info != NULL) {
+ p_ccb->ertm_info.allowed_modes =
+ (chan_mode_mask) ? chan_mode_mask : (UINT8)L2CAP_FCR_CHAN_OPT_BASIC;
+ }
+
+ if (is_server)
+ {
+ p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE; /* assume btm/l2cap would handle it */
+ p_ccb->con_state = GAP_CCB_STATE_LISTENING;
+ return (p_ccb->gap_handle);
+ }
+ else
+ {
+ /* We are the originator of this connection */
+ p_ccb->con_flags = GAP_CCB_FLAGS_IS_ORIG;
+
+ /* Transition to the next appropriate state, waiting for connection confirm. */
+ p_ccb->con_state = GAP_CCB_STATE_CONN_SETUP;
+
+ /* mark security done flag, when security is not required */
+ if ((security & (BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT) ) == 0)
+ p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE;
+
+ /* Check if L2CAP started the connection process */
+ if (p_rem_bda && ((cid = L2CA_CONNECT_REQ (p_ccb->psm, p_rem_bda, &p_ccb->ertm_info, &bt_uuid)) != 0))
+ {
+ p_ccb->connection_id = cid;
+ return (p_ccb->gap_handle);
+ }
+ else
+ {
+ gap_release_ccb (p_ccb);
+ return (GAP_INVALID_HANDLE);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnClose
+**
+** Description This function is called to close a connection.
+**
+** Parameters: handle - Handle of the connection returned by GAP_ConnOpen
+**
+** Returns BT_PASS - closed OK
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+UINT16 GAP_ConnClose (UINT16 gap_handle)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+
+ GAP_TRACE_EVENT ("GAP_CONN - close handle: 0x%x", gap_handle);
+
+ if (p_ccb)
+ {
+ /* Check if we have a connection ID */
+ if (p_ccb->con_state != GAP_CCB_STATE_LISTENING)
+ L2CA_DISCONNECT_REQ (p_ccb->connection_id);
+
+ gap_release_ccb (p_ccb);
+
+ return (BT_PASS);
+ }
+
+ return (GAP_ERR_BAD_HANDLE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnReadData
+**
+** Description Normally not GKI aware application will call this function
+** after receiving GAP_EVT_RXDATA event.
+**
+** Parameters: handle - Handle of the connection returned in the Open
+** p_data - Data area
+** max_len - Byte count requested
+** p_len - Byte count received
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_NO_DATA_AVAIL - no data available
+**
+*******************************************************************************/
+UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+ BT_HDR *p_buf;
+ UINT16 copy_len;
+
+ if (!p_ccb)
+ return (GAP_ERR_BAD_HANDLE);
+
+ *p_len = 0;
+
+ p_buf = (BT_HDR *)GKI_getfirst (&p_ccb->rx_queue);
+ if (!p_buf)
+ return (GAP_NO_DATA_AVAIL);
+
+ GKI_disable();
+
+ while (max_len && p_buf)
+ {
+ copy_len = (p_buf->len > max_len)?max_len:p_buf->len;
+ max_len -= copy_len;
+ *p_len += copy_len;
+ if (p_data)
+ {
+ memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, copy_len);
+ p_data += copy_len;
+ }
+
+ if (p_buf->len > copy_len)
+ {
+ p_buf->offset += copy_len;
+ p_buf->len -= copy_len;
+ break;
+ }
+ else
+ {
+ if (max_len)
+ {
+ p_buf = (BT_HDR *)GKI_getnext (p_buf);
+ }
+ GKI_freebuf (GKI_dequeue (&p_ccb->rx_queue));
+ }
+ }
+
+ p_ccb->rx_queue_size -= *p_len;
+
+ GKI_enable();
+
+ GAP_TRACE_EVENT ("GAP_ConnReadData - rx_queue_size left=%d, *p_len=%d",
+ p_ccb->rx_queue_size, *p_len);
+
+ return (BT_PASS);
+}
+
+/*******************************************************************************
+**
+** Function GAP_GetRxQueueCnt
+**
+** Description This function return number of bytes on the rx queue.
+**
+** Parameters: handle - Handle returned in the GAP_ConnOpen
+** p_rx_queue_count - Pointer to return queue count in.
+**
+**
+*******************************************************************************/
+int GAP_GetRxQueueCnt (UINT16 handle, UINT32 *p_rx_queue_count)
+{
+ tGAP_CCB *p_ccb;
+ int rc = BT_PASS;
+
+ /* Check that handle is valid */
+ if (handle < GAP_MAX_CONNECTIONS)
+ {
+ p_ccb = &gap_cb.conn.ccb_pool[handle];
+
+ if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED)
+ {
+ *p_rx_queue_count = p_ccb->rx_queue_size;
+ }
+ else
+ rc = GAP_INVALID_HANDLE;
+ }
+ else
+ rc = GAP_INVALID_HANDLE;
+
+ GAP_TRACE_EVENT ("GAP_GetRxQueueCnt - rc = 0x%04x, rx_queue_count=%d",
+ rc , *p_rx_queue_count);
+
+ return (rc);
+}
+
+/*******************************************************************************
+**
+** Function GAP_ConnBTRead
+**
+** Description Bluetooth aware applications will call this function after receiving
+** GAP_EVT_RXDATA event.
+**
+** Parameters: handle - Handle of the connection returned in the Open
+** pp_buf - pointer to address of buffer with data,
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_NO_DATA_AVAIL - no data available
+**
+*******************************************************************************/
+UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+ BT_HDR *p_buf;
+
+ if (!p_ccb)
+ return (GAP_ERR_BAD_HANDLE);
+
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->rx_queue);
+
+ if (p_buf)
+ {
+ *pp_buf = p_buf;
+
+ p_ccb->rx_queue_size -= p_buf->len;
+ return (BT_PASS);
+ }
+ else
+ {
+ *pp_buf = NULL;
+ return (GAP_NO_DATA_AVAIL);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnBTWrite
+**
+** Description Bluetooth Aware applications can call this function to write data.
+**
+** Parameters: handle - Handle of the connection returned in the Open
+** p_buf - pointer to address of buffer with data,
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_ERR_BAD_STATE - connection not established
+** GAP_INVALID_BUF_OFFSET - buffer offset is invalid
+*******************************************************************************/
+UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+
+ if (!p_ccb)
+ {
+ GKI_freebuf (p_buf);
+ return (GAP_ERR_BAD_HANDLE);
+ }
+
+ if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED)
+ {
+ GKI_freebuf (p_buf);
+ return (GAP_ERR_BAD_STATE);
+ }
+
+ if (p_buf->offset < L2CAP_MIN_OFFSET)
+ {
+ GKI_freebuf (p_buf);
+ return (GAP_ERR_BUF_OFFSET);
+ }
+
+ GKI_enqueue (&p_ccb->tx_queue, p_buf);
+
+ if (p_ccb->is_congested)
+ {
+ return (BT_PASS);
+ }
+
+ /* Send the buffer through L2CAP */
+#if (GAP_CONN_POST_EVT_INCLUDED == TRUE)
+ gap_send_event (gap_handle);
+#else
+ while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL)
+ {
+ UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf);
+
+ if (status == L2CAP_DW_CONGESTED)
+ {
+ p_ccb->is_congested = TRUE;
+ break;
+ }
+ else if (status != L2CAP_DW_SUCCESS)
+ return (GAP_ERR_BAD_STATE);
+ }
+#endif
+ return (BT_PASS);
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnWriteData
+**
+** Description Normally not GKI aware application will call this function
+** to send data to the connection.
+**
+** Parameters: handle - Handle of the connection returned in the Open
+** p_data - Data area
+** max_len - Byte count requested
+** p_len - Byte count received
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_ERR_BAD_STATE - connection not established
+** GAP_CONGESTION - system is congested
+**
+*******************************************************************************/
+UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data, UINT16 max_len, UINT16 *p_len)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+ BT_HDR *p_buf;
+
+ *p_len = 0;
+
+ if (!p_ccb)
+ return (GAP_ERR_BAD_HANDLE);
+
+ if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED)
+ return (GAP_ERR_BAD_STATE);
+
+ while (max_len)
+ {
+ if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (p_ccb->ertm_info.user_tx_pool_id)) == NULL)
+ return (GAP_ERR_CONGESTED);
+ }
+ else
+ {
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf (GAP_DATA_POOL_ID)) == NULL)
+ return (GAP_ERR_CONGESTED);
+ }
+
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p_buf->len = (p_ccb->rem_mtu_size < max_len) ? p_ccb->rem_mtu_size : max_len;
+ p_buf->event = BT_EVT_TO_BTU_SP_DATA;
+
+ memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len);
+
+ *p_len += p_buf->len;
+ max_len -= p_buf->len;
+ p_data += p_buf->len;
+
+ GAP_TRACE_EVENT ("GAP_WriteData %d bytes", p_buf->len);
+
+ GKI_enqueue (&p_ccb->tx_queue, p_buf);
+ }
+
+ if (p_ccb->is_congested)
+ {
+ return (BT_PASS);
+ }
+
+ /* Send the buffer through L2CAP */
+#if (GAP_CONN_POST_EVT_INCLUDED == TRUE)
+ gap_send_event (gap_handle);
+#else
+ while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL)
+ {
+ UINT8 status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf);
+
+ if (status == L2CAP_DW_CONGESTED)
+ {
+ p_ccb->is_congested = TRUE;
+ break;
+ }
+ else if (status != L2CAP_DW_SUCCESS)
+ return (GAP_ERR_BAD_STATE);
+ }
+#endif
+ return (BT_PASS);
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnReconfig
+**
+** Description Applications can call this function to reconfigure the connection.
+**
+** Parameters: handle - Handle of the connection
+** p_cfg - Pointer to new configuration
+**
+** Returns BT_PASS - config process started
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+UINT16 GAP_ConnReconfig (UINT16 gap_handle, tL2CAP_CFG_INFO *p_cfg)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+
+ if (!p_ccb)
+ return (GAP_ERR_BAD_HANDLE);
+
+ p_ccb->cfg = *p_cfg;
+
+ if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED)
+ L2CA_CONFIG_REQ (p_ccb->connection_id, p_cfg);
+
+ return (BT_PASS);
+}
+
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnSetIdleTimeout
+**
+** Description Higher layers call this function to set the idle timeout for
+** a connection, or for all future connections. The "idle timeout"
+** is the amount of time that a connection can remain up with
+** no L2CAP channels on it. A timeout of zero means that the
+** connection will be torn down immediately when the last channel
+** is removed. A timeout of 0xFFFF means no timeout. Values are
+** in seconds.
+**
+** Parameters: handle - Handle of the connection
+** timeout - in secs
+** 0 = immediate disconnect when last channel is removed
+** 0xFFFF = no idle timeout
+**
+** Returns BT_PASS - config process started
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+UINT16 GAP_ConnSetIdleTimeout (UINT16 gap_handle, UINT16 timeout)
+{
+ tGAP_CCB *p_ccb;
+
+ if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL)
+ return (GAP_ERR_BAD_HANDLE);
+
+ if (L2CA_SetIdleTimeout (p_ccb->connection_id, timeout, FALSE))
+ return (BT_PASS);
+ else
+ return (GAP_ERR_BAD_HANDLE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetRemoteAddr
+**
+** Description This function is called to get the remote BD address
+** of a connection.
+**
+** Parameters: handle - Handle of the connection returned by GAP_ConnOpen
+**
+** Returns BT_PASS - closed OK
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+UINT8 *GAP_ConnGetRemoteAddr (UINT16 gap_handle)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (gap_handle);
+
+ GAP_TRACE_EVENT ("GAP_ConnGetRemoteAddr gap_handle = %d", gap_handle);
+
+ if ((p_ccb) && (p_ccb->con_state > GAP_CCB_STATE_LISTENING))
+ {
+ GAP_TRACE_EVENT("GAP_ConnGetRemoteAddr bda :0x%02x:0x%02x:0x%02x:0x%02x:0x%02x:0x%02x\n", \
+ p_ccb->rem_dev_address[0],p_ccb->rem_dev_address[1],p_ccb->rem_dev_address[2],
+ p_ccb->rem_dev_address[3],p_ccb->rem_dev_address[4],p_ccb->rem_dev_address[5]);
+ return (p_ccb->rem_dev_address);
+ }
+ else
+ {
+ GAP_TRACE_EVENT ("GAP_ConnGetRemoteAddr return Error ");
+ return (NULL);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetRemMtuSize
+**
+** Description Returns the remote device's MTU size
+**
+** Parameters: handle - Handle of the connection
+**
+** Returns UINT16 - maximum size buffer that can be transmitted to the peer
+**
+*******************************************************************************/
+UINT16 GAP_ConnGetRemMtuSize (UINT16 gap_handle)
+{
+ tGAP_CCB *p_ccb;
+
+ if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL)
+ return (0);
+
+ return (p_ccb->rem_mtu_size);
+}
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetL2CAPCid
+**
+** Description Returns the L2CAP channel id
+**
+** Parameters: handle - Handle of the connection
+**
+** Returns UINT16 - The L2CAP channel id
+** 0, if error
+**
+*******************************************************************************/
+UINT16 GAP_ConnGetL2CAPCid (UINT16 gap_handle)
+{
+ tGAP_CCB *p_ccb;
+
+ if ((p_ccb = gap_find_ccb_by_handle (gap_handle)) == NULL)
+ return (0);
+
+ return (p_ccb->connection_id);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_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 gap_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
+{
+ UINT16 xx;
+ tGAP_CCB *p_ccb;
+ tBT_UUID bt_uuid = {2, {GAP_PROTOCOL_ID}};
+
+ /* See if we have a CCB listening for the connection */
+ for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if ((p_ccb->con_state == GAP_CCB_STATE_LISTENING)
+ && (p_ccb->psm == psm)
+ && ((p_ccb->rem_addr_specified == FALSE)
+ || (!memcmp (bd_addr, p_ccb->rem_dev_address, BD_ADDR_LEN))))
+ break;
+ }
+
+ if (xx == GAP_MAX_CONNECTIONS)
+ {
+ GAP_TRACE_WARNING("*******");
+ GAP_TRACE_WARNING("WARNING: GAP Conn Indication for Unexpected Bd Addr...Disconnecting");
+ GAP_TRACE_WARNING("*******");
+
+ /* Disconnect because it is an unexpected connection */
+ L2CA_DISCONNECT_REQ (l2cap_cid);
+ return;
+ }
+
+ /* Transition to the next appropriate state, waiting for config setup. */
+ p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP;
+
+ /* Save the BD Address and Channel ID. */
+ memcpy (&p_ccb->rem_dev_address[0], bd_addr, BD_ADDR_LEN);
+ p_ccb->connection_id = l2cap_cid;
+
+ /* Send response to the L2CAP layer. */
+ L2CA_CONNECT_RSP (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK, &p_ccb->ertm_info, &bt_uuid);
+
+ GAP_TRACE_EVENT("GAP_CONN - Rcvd L2CAP conn ind, CID: 0x%x", p_ccb->connection_id);
+
+ /* Send a Configuration Request. */
+ L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg);
+}
+
+/*******************************************************************************
+**
+** Function gap_checks_con_flags
+**
+** Description This function processes the L2CAP configuration indication
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_checks_con_flags (tGAP_CCB *p_ccb)
+{
+ GAP_TRACE_EVENT ("gap_checks_con_flags conn_flags:0x%x, ", p_ccb->con_flags);
+ /* if all the required con_flags are set, report the OPEN event now */
+ if ((p_ccb->con_flags & GAP_CCB_FLAGS_CONN_DONE) == GAP_CCB_FLAGS_CONN_DONE)
+ {
+ p_ccb->con_state = GAP_CCB_STATE_CONNECTED;
+
+ p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_OPENED);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_sec_check_complete
+**
+** Description The function called when Security Manager finishes
+** verification of the service side connection
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
+{
+ tGAP_CCB *p_ccb = (tGAP_CCB *)p_ref_data;
+ UNUSED(bd_addr);
+ UNUSED (transport);
+
+ GAP_TRACE_EVENT ("gap_sec_check_complete conn_state:%d, conn_flags:0x%x, status:%d",
+ p_ccb->con_state, p_ccb->con_flags, res);
+ if (p_ccb->con_state == GAP_CCB_STATE_IDLE)
+ return;
+
+ if (res == BTM_SUCCESS)
+ {
+ p_ccb->con_flags |= GAP_CCB_FLAGS_SEC_DONE;
+ gap_checks_con_flags (p_ccb);
+ }
+ else
+ {
+ /* security failed - disconnect the channel */
+ L2CA_DISCONNECT_REQ (p_ccb->connection_id);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_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 gap_connect_cfm (UINT16 l2cap_cid, UINT16 result)
+{
+ tGAP_CCB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL)
+ return;
+
+ /* initiate security process, if needed */
+ if ( (p_ccb->con_flags & GAP_CCB_FLAGS_SEC_DONE) == 0)
+ {
+ btm_sec_mx_access_request (p_ccb->rem_dev_address, p_ccb->psm, TRUE,
+ 0, 0, &gap_sec_check_complete, p_ccb);
+ }
+
+ /* 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 == GAP_CCB_STATE_CONN_SETUP))
+ {
+ p_ccb->con_state = GAP_CCB_STATE_CFG_SETUP;
+
+ /* Send a Configuration Request. */
+ L2CA_CONFIG_REQ (l2cap_cid, &p_ccb->cfg);
+ }
+ else
+ {
+ /* Tell the user if he has a callback */
+ if (p_ccb->p_callback)
+ (*p_ccb->p_callback) (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED);
+
+ gap_release_ccb (p_ccb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_config_ind
+**
+** Description This function processes the L2CAP configuration indication
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tGAP_CCB *p_ccb;
+ UINT16 local_mtu_size;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL)
+ return;
+
+ /* Remember the remote MTU size */
+
+ if (p_ccb->cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ {
+ local_mtu_size = GKI_get_pool_bufsize (p_ccb->ertm_info.user_tx_pool_id)
+ - sizeof(BT_HDR) - L2CAP_MIN_OFFSET;
+ }
+ else
+ local_mtu_size = L2CAP_MTU_SIZE;
+
+ if ((!p_cfg->mtu_present)||(p_cfg->mtu > local_mtu_size))
+ {
+ p_ccb->rem_mtu_size = local_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;
+ p_cfg->fcs_present = FALSE;
+
+ L2CA_CONFIG_RSP (l2cap_cid, p_cfg);
+
+ p_ccb->con_flags |= GAP_CCB_FLAGS_HIS_CFG_DONE;
+
+ gap_checks_con_flags (p_ccb);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_config_cfm
+**
+** Description This function processes the L2CAP configuration confirmation
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tGAP_CCB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL)
+ return;
+
+ if (p_cfg->result == L2CAP_CFG_OK)
+ {
+ p_ccb->con_flags |= GAP_CCB_FLAGS_MY_CFG_DONE;
+
+
+ if (p_ccb->cfg.fcr_present)
+ p_ccb->cfg.fcr.mode = p_cfg->fcr.mode;
+ else
+ p_ccb->cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+
+ gap_checks_con_flags (p_ccb);
+ }
+ else
+ {
+ p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED);
+ gap_release_ccb (p_ccb);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function gap_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 gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
+{
+ tGAP_CCB *p_ccb;
+
+ GAP_TRACE_EVENT ("GAP_CONN - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL)
+ return;
+
+ if (ack_needed)
+ L2CA_DISCONNECT_RSP (l2cap_cid);
+
+ p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED);
+ gap_release_ccb (p_ccb);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_data_ind
+**
+** Description This function is called when data is received from L2CAP.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
+{
+ tGAP_CCB *p_ccb;
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (l2cap_cid)) == NULL)
+ {
+ GKI_freebuf (p_msg);
+ return;
+ }
+
+ if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED)
+ {
+ GKI_enqueue (&p_ccb->rx_queue, p_msg);
+
+ p_ccb->rx_queue_size += p_msg->len;
+ /*
+ GAP_TRACE_EVENT ("gap_data_ind - rx_queue_size=%d, msg len=%d",
+ p_ccb->rx_queue_size, p_msg->len);
+ */
+
+ p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_DATA_AVAIL);
+ }
+ else
+ {
+ GKI_freebuf (p_msg);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function gap_congestion_ind
+**
+** Description This is a callback function called by L2CAP when
+** data L2CAP congestion status changes
+**
+*******************************************************************************/
+static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested)
+{
+ tGAP_CCB *p_ccb;
+ UINT16 event;
+ BT_HDR *p_buf;
+ UINT8 status;
+
+ GAP_TRACE_EVENT ("GAP_CONN - Rcvd L2CAP Is Congested (%d), CID: 0x%x",
+ is_congested, lcid);
+
+ /* Find CCB based on CID */
+ if ((p_ccb = gap_find_ccb_by_cid (lcid)) == NULL)
+ return;
+
+ p_ccb->is_congested = is_congested;
+
+ event = (is_congested) ? GAP_EVT_CONN_CONGESTED : GAP_EVT_CONN_UNCONGESTED;
+ p_ccb->p_callback (p_ccb->gap_handle, event);
+
+ if (!is_congested)
+ {
+ while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL)
+ {
+ status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf);
+
+ if (status == L2CAP_DW_CONGESTED)
+ {
+ p_ccb->is_congested = TRUE;
+ break;
+ }
+ else if (status != L2CAP_DW_SUCCESS)
+ break;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function gap_find_ccb_by_cid
+**
+** Description This function searches the CCB table for an entry with the
+** passed CID.
+**
+** Returns the CCB address, or NULL if not found.
+**
+*******************************************************************************/
+static tGAP_CCB *gap_find_ccb_by_cid (UINT16 cid)
+{
+ UINT16 xx;
+ tGAP_CCB *p_ccb;
+
+ /* Look through each connection control block */
+ for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->connection_id == cid))
+ return (p_ccb);
+ }
+
+ /* If here, not found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_find_ccb_by_handle
+**
+** Description This function searches the CCB table for an entry with the
+** passed handle.
+**
+** Returns the CCB address, or NULL if not found.
+**
+*******************************************************************************/
+static tGAP_CCB *gap_find_ccb_by_handle (UINT16 handle)
+{
+ tGAP_CCB *p_ccb;
+
+ /* Check that handle is valid */
+ if (handle < GAP_MAX_CONNECTIONS)
+ {
+ p_ccb = &gap_cb.conn.ccb_pool[handle];
+
+ if (p_ccb->con_state != GAP_CCB_STATE_IDLE)
+ return (p_ccb);
+ }
+
+ /* If here, handle points to invalid connection */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_allocate_ccb
+**
+** Description This function allocates a new CCB.
+**
+** Returns CCB address, or NULL if none available.
+**
+*******************************************************************************/
+static tGAP_CCB *gap_allocate_ccb (void)
+{
+ UINT16 xx;
+ tGAP_CCB *p_ccb;
+
+ /* Look through each connection control block for a free one */
+ for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if (p_ccb->con_state == GAP_CCB_STATE_IDLE)
+ {
+ memset (p_ccb, 0, sizeof (tGAP_CCB));
+
+ p_ccb->gap_handle = xx;
+ p_ccb->rem_mtu_size = L2CAP_MTU_SIZE;
+
+ return (p_ccb);
+ }
+ }
+
+ /* If here, no free CCB found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_release_ccb
+**
+** Description This function releases a CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gap_release_ccb (tGAP_CCB *p_ccb)
+{
+ UINT16 xx;
+ UINT16 psm = p_ccb->psm;
+ UINT8 service_id = p_ccb->service_id;
+
+ /* Drop any buffers we may be holding */
+ p_ccb->rx_queue_size = 0;
+
+ while (p_ccb->rx_queue._p_first)
+ GKI_freebuf (GKI_dequeue (&p_ccb->rx_queue));
+
+ while (p_ccb->tx_queue._p_first)
+ GKI_freebuf (GKI_dequeue (&p_ccb->tx_queue));
+
+ p_ccb->con_state = GAP_CCB_STATE_IDLE;
+
+ /* If no-one else is using the PSM, deregister from L2CAP */
+ for (xx = 0, p_ccb = gap_cb.conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++)
+ {
+ if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->psm == psm))
+ return;
+ }
+
+ /* Free the security record for this PSM */
+ BTM_SecClrService(service_id);
+ L2CA_DEREGISTER (psm);
+}
+
+#if (GAP_CONN_POST_EVT_INCLUDED == TRUE)
+
+/*******************************************************************************
+**
+** Function gap_send_event
+**
+** Description Send BT_EVT_TO_GAP_MSG event to BTU task
+**
+** Returns None
+**
+*******************************************************************************/
+void gap_send_event (UINT16 gap_handle)
+{
+ BT_HDR *p_msg;
+
+ if ((p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE)) != NULL)
+ {
+ p_msg->event = BT_EVT_TO_GAP_MSG;
+ p_msg->len = 0;
+ p_msg->offset = 0;
+ p_msg->layer_specific = gap_handle;
+
+ GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg);
+ }
+ else
+ {
+ GAP_TRACE_ERROR("Unable to allocate message buffer for event.");
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_proc_btu_event
+**
+** Description Event handler for BT_EVT_TO_GAP_MSG event from BTU task
+**
+** Returns None
+**
+*******************************************************************************/
+void gap_proc_btu_event(BT_HDR *p_msg)
+{
+ tGAP_CCB *p_ccb = gap_find_ccb_by_handle (p_msg->layer_specific);
+ UINT8 status;
+ BT_HDR *p_buf;
+
+ if (!p_ccb)
+ {
+ return;
+ }
+
+ if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED)
+ {
+ return;
+ }
+
+ if (p_ccb->is_congested)
+ {
+ return;
+ }
+
+ /* Send the buffer through L2CAP */
+
+ while ((p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->tx_queue)) != NULL)
+ {
+ status = L2CA_DATA_WRITE (p_ccb->connection_id, p_buf);
+
+ if (status == L2CAP_DW_CONGESTED)
+ {
+ p_ccb->is_congested = TRUE;
+ break;
+ }
+ else if (status != L2CAP_DW_SUCCESS)
+ break;
+ }
+
+}
+#endif /* (GAP_CONN_POST_EVT_INCLUDED == TRUE) */
+#endif /* GAP_CONN_INCLUDED */
diff --git a/stack/gap/gap_int.h b/stack/gap/gap_int.h
index 40fe96a08..0f63f3aba 100644
--- a/stack/gap/gap_int.h
+++ b/stack/gap/gap_int.h
@@ -24,6 +24,77 @@
#include "gap_api.h"
#include "gki.h"
#include "gatt_api.h"
+#define GAP_MAX_BLOCKS 2 /* Concurrent GAP commands pending at a time*/
+/* Define the Generic Access Profile control structure */
+typedef struct
+{
+ void *p_data; /* Pointer to any data returned in callback */
+ tGAP_CALLBACK *gap_cback; /* Pointer to users callback function */
+ tGAP_CALLBACK *gap_inq_rslt_cback; /* Used for inquiry results */
+ UINT16 event; /* Passed back in the callback */
+ UINT8 index; /* Index of this control block and callback */
+ BOOLEAN in_use; /* True when structure is allocated */
+} tGAP_INFO;
+
+/* Define the control block for the FindAddrByName operation (Only 1 active at a time) */
+typedef struct
+{
+ tGAP_CALLBACK *p_cback;
+ tBTM_INQ_INFO *p_cur_inq; /* Pointer to the current inquiry database entry */
+ tGAP_FINDADDR_RESULTS results;
+ BOOLEAN in_use;
+} tGAP_FINDADDR_CB;
+
+/* Define the GAP Connection Control Block.
+*/
+typedef struct
+{
+#define GAP_CCB_STATE_IDLE 0
+#define GAP_CCB_STATE_LISTENING 1
+#define GAP_CCB_STATE_CONN_SETUP 2
+#define GAP_CCB_STATE_CFG_SETUP 3
+#define GAP_CCB_STATE_WAIT_SEC 4
+#define GAP_CCB_STATE_CONNECTED 5
+ UINT8 con_state;
+
+#define GAP_CCB_FLAGS_IS_ORIG 0x01
+#define GAP_CCB_FLAGS_HIS_CFG_DONE 0x02
+#define GAP_CCB_FLAGS_MY_CFG_DONE 0x04
+#define GAP_CCB_FLAGS_SEC_DONE 0x08
+#define GAP_CCB_FLAGS_CONN_DONE 0x0E
+ UINT8 con_flags;
+
+ UINT8 service_id; /* Used by BTM */
+ UINT16 gap_handle; /* GAP handle */
+ UINT16 connection_id; /* L2CAP CID */
+ BOOLEAN rem_addr_specified;
+ UINT8 chan_mode_mask; /* Supported channel modes (FCR) */
+ BD_ADDR rem_dev_address;
+ UINT16 psm;
+ UINT16 rem_mtu_size;
+
+ BOOLEAN is_congested;
+ BUFFER_Q tx_queue; /* Queue of buffers waiting to be sent */
+ BUFFER_Q rx_queue; /* Queue of buffers waiting to be read */
+
+ UINT32 rx_queue_size; /* Total data count in rx_queue */
+
+ tGAP_CONN_CALLBACK *p_callback; /* Users callback function */
+
+ tL2CAP_CFG_INFO cfg; /* Configuration */
+ tL2CAP_ERTM_INFO ertm_info; /* Pools and modes for ertm */
+} tGAP_CCB;
+
+typedef struct
+{
+#if AMP_INCLUDED == TRUE
+ tAMP_APPL_INFO reg_info;
+#else
+ tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */
+#endif
+ tGAP_CCB ccb_pool[GAP_MAX_CONNECTIONS];
+} tGAP_CONN;
+
#if BLE_INCLUDED == TRUE
#define GAP_MAX_CHAR_NUM 5
@@ -68,7 +139,15 @@ typedef struct
typedef struct
{
+ tGAP_INFO blk[GAP_MAX_BLOCKS];
+ tBTM_CMPL_CB *btm_cback[GAP_MAX_BLOCKS];
UINT8 trace_level;
+ tGAP_FINDADDR_CB findaddr_cb; /* Contains the control block for finding a device addr */
+ tBTM_INQ_INFO *cur_inqptr;
+
+#if GAP_CONN_INCLUDED == TRUE
+ tGAP_CONN conn;
+#endif
/* LE GAP attribute database */
#if BLE_INCLUDED == TRUE
diff --git a/stack/gap/gap_utils.c b/stack/gap/gap_utils.c
new file mode 100644
index 000000000..e7dfb200c
--- /dev/null
+++ b/stack/gap/gap_utils.c
@@ -0,0 +1,436 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2013 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.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bt_target.h"
+#include "bt_utils.h"
+#include "gap_int.h"
+
+/*****************************************************************************/
+/* G L O B A L GAP D A T A */
+/*****************************************************************************/
+tGAP_CB gap_cb;
+
+#if 0
+/*****************************************************************************
+** Callbacks passed to BTM -
+** There are different callbacks based on the control block index so that
+** more than one command can be pending at a time.
+** NOTE: There must be 1 callback for each control block defined
+** GAP_MAX_BLOCKS
+**
+** Also, the inquiry results event has its own callback; Not handled here!
+******************************************************************************/
+static void btm_cback(UINT16 index, void *p_data)
+{
+ tGAP_INFO *p_cb;
+ tGAP_INQ_CMPL inq_cmpl;
+
+ /* Make sure that the index is valid AND it is in use */
+ if (index < GAP_MAX_BLOCKS && gap_cb.blk[index].in_use)
+ {
+ p_cb = &gap_cb.blk[index];
+
+ /* If the callback is non-NULL, call it with the specified event */
+ switch (p_cb->event)
+ {
+
+ case GAP_EVT_INQUIRY_COMPLETE:
+ /* pass the number of results to caller */
+ inq_cmpl.num_results = ((tBTM_INQUIRY_CMPL *)p_data)->num_resp;
+
+ inq_cmpl.status = (((tBTM_INQUIRY_CMPL *)p_data)->status == BTM_SUCCESS) ? BT_PASS : GAP_ERR_PROCESSING;
+
+ p_data = &inq_cmpl;
+
+ GAP_TRACE_EVENT(" GAP Inquiry Complete Event (Status 0x%04x, Result(s) %d)",
+ inq_cmpl.status, inq_cmpl.num_results);
+ break;
+
+ case GAP_EVT_DISCOVERY_COMPLETE:
+ if (*((UINT16 *) p_data))
+ {
+ GAP_TRACE_EVENT(" GAP Discovery Complete Event(SDP Result: 0x%04x)", *((UINT16 *) p_data));
+ }
+ else
+ {
+ GAP_TRACE_EVENT(" GAP Discovery Successfully Completed");
+ }
+
+ break;
+
+ case GAP_EVT_REM_NAME_COMPLETE:
+ /* override the BTM error code with a GAP error code */
+ ((tGAP_REMOTE_DEV_NAME *)p_data)->status =
+ gap_convert_btm_status ((tBTM_STATUS)((tBTM_REMOTE_DEV_NAME *)p_data)->status);
+
+ GAP_TRACE_EVENT(" GAP Remote Name Complete Event (status 0x%04x)", ((tGAP_REMOTE_DEV_NAME *)p_data)->status);
+
+ break;
+ };
+
+ if (p_cb->gap_cback)
+ p_cb->gap_cback(p_cb->event, p_data);
+
+ /* Deallocate the control block */
+ gap_free_cb(p_cb);
+ }
+}
+
+
+/*** Callback functions for BTM_CMPL_CB ***/
+void gap_btm_cback0(void *p1)
+{
+ btm_cback(0, p1);
+}
+
+#if GAP_MAX_BLOCKS > 1
+void gap_btm_cback1(void *p1)
+{
+ btm_cback(1, p1);
+}
+#endif
+#if GAP_MAX_BLOCKS > 2
+void gap_btm_cback2(void *p1)
+{
+ btm_cback(2, p1);
+}
+#endif
+
+/* There is only one instance of this because only 1 inquiry can be active at a time */
+void gap_inq_results_cb(tBTM_INQ_RESULTS *p_results, UINT8 *p_eir)
+{
+ tGAP_INFO *p_cb;
+ UINT8 index;
+ UNUSED(p_eir);
+
+ GAP_TRACE_EVENT ("GAP Inquiry Results Callback (bdaddr [%02x%02x%02x%02x%02x%02x])",
+ p_results->remote_bd_addr[0], p_results->remote_bd_addr[1],
+ p_results->remote_bd_addr[2], p_results->remote_bd_addr[3],
+ p_results->remote_bd_addr[4], p_results->remote_bd_addr[5]);
+ GAP_TRACE_EVENT (" (COD [%02x%02x%02x], clkoff 0x%04x)",
+ p_results->dev_class[0], p_results->dev_class[1], p_results->dev_class[2],
+ p_results->clock_offset);
+
+ /* Find the control block which has an Inquiry Active and call its results callback */
+ for (index = 0, p_cb = &gap_cb.blk[0]; index < GAP_MAX_BLOCKS; index++, p_cb++)
+ {
+ /* Look for the control block that is using inquiry */
+ if (p_cb->in_use && (p_cb->event == GAP_EVT_INQUIRY_COMPLETE))
+ {
+ /* Notify the higher layer if they care */
+ if (p_cb->gap_inq_rslt_cback)
+ p_cb->gap_inq_rslt_cback (GAP_EVT_INQUIRY_RESULTS, (tGAP_INQ_RESULTS *)p_results);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function gap_find_addr_name_cb
+**
+** Description Processes the remote name request event when the Find Addr by Name
+** request is active. The following procedure takes place:
+** 1. Check the resulting name (If return status is ok)
+** 2. If name matches requested name, we're done, call the appl's callback
+** with the BD ADDR.
+** 3. Otherwise get the next BD ADDR out of the inquiry database and intiate
+** another remote name request.
+** 4. If there are no more BD ADDRs, then call the appl's callback with a FAIL
+** status.
+**
+** Returns void
+**
+*******************************************************************************/
+void gap_find_addr_name_cb (tBTM_REMOTE_DEV_NAME *p)
+{
+ tGAP_FINDADDR_CB *p_cb = &gap_cb.findaddr_cb;
+ tGAP_FINDADDR_RESULTS *p_result = &p_cb->results;
+
+ if (p_cb->in_use)
+ {
+ if (p->status == BTM_SUCCESS)
+ {
+ GAP_TRACE_EVENT(" GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x, Name [%s])",
+ p->status, p->remote_bd_name);
+
+ /* See if the returned name matches the desired name; if not initiate another search */
+ if (!strncmp ((char *)p_result->devname, (char *) p->remote_bd_name, strlen ((char *) p_result->devname)))
+ {
+ /* We found the device! Copy it into the return structure */
+ memcpy (p_result->bd_addr, p_cb->p_cur_inq->results.remote_bd_addr, BD_ADDR_LEN);
+ p_result->status = BT_PASS;
+ }
+ else /* The name doesn't match so initiate another search */
+ {
+ /* Get the device address of the next database entry */
+ if ((p_cb->p_cur_inq = BTM_InqDbNext(p_cb->p_cur_inq)) != NULL)
+ {
+ if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
+ (tBTM_CMPL_CB *) gap_find_addr_name_cb, BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED)
+ return; /* This routine will get called again with the next results */
+ else
+ p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
+ }
+ else
+ p_result->status = GAP_EOINQDB; /* No inquiry results; we're done! */
+ }
+ }
+ else
+ {
+ GAP_TRACE_EVENT(" GAP: FindAddrByName Rem Name Cmpl Evt (Status 0x%04x)", p->status);
+ p_result->status = gap_convert_btm_status ((tBTM_STATUS) p->status);
+ }
+
+ /* If this code is reached, the process has completed so call the appl's callback with results */
+ if (p_cb->p_cback)
+ p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
+
+ /* Clear out the control block */
+ p_cb->in_use = FALSE;
+ p_cb->p_cback = (tGAP_CALLBACK *) NULL;
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_find_addr_inq_cb
+**
+** Description Processes the inquiry complete event when the Find Addr by Name
+** request is active. This callback performs one of the two following
+** steps:
+** 1. If the remote name is retrieved automatically, the DB is searched
+** immediately, and the results are returned in the appls callback.
+**
+** 2. If remote name is not automatic, retrieve the first BTM INQ
+** database entry and initiate a remote name request.
+**
+** Returns void
+**
+*******************************************************************************/
+void gap_find_addr_inq_cb (tBTM_INQUIRY_CMPL *p)
+{
+ tGAP_FINDADDR_CB *p_cb = &gap_cb.findaddr_cb;
+ tGAP_FINDADDR_RESULTS *p_result = &p_cb->results;
+
+ if (p_cb->in_use)
+ {
+
+ GAP_TRACE_EVENT(" GAP: FindAddrByName Inq Cmpl Evt (Status 0x%04x, Result(s) %d)",
+ p->status, p->num_resp);
+
+ if (p->status == BTM_SUCCESS)
+ {
+ /* Step 1: If automatically retrieving remote names then search the local database */
+ if ((p_result->status = gap_find_local_addr_by_name (p_result->devname, p_result->bd_addr)) == GAP_NO_DATA_AVAIL)
+ {
+ /* Step 2: The name is not stored automatically, so a search of all devices needs to
+ * be initiated.
+ */
+ if ((p_cb->p_cur_inq = BTM_InqDbFirst()) != NULL)
+ {
+ if ((BTM_ReadRemoteDeviceName (p_cb->p_cur_inq->results.remote_bd_addr,
+ (tBTM_CMPL_CB *) gap_find_addr_name_cb, BT_TRANSPORT_BR_EDR)) == BTM_CMD_STARTED)
+ return; /* Wait for the response in gap_find_addr_name_cb() */
+ else
+ p_result->status = gap_convert_btm_status (p->status);
+ }
+ else
+ p_result->status = GAP_EOINQDB; /* No inquiry results; we're done! */
+ }
+ }
+ else
+ p_result->status = gap_convert_btm_status (p->status);
+
+ /* If this code is reached, the process has completed so call the appl's callback with results */
+ if (p_cb->p_cback)
+ p_cb->p_cback (GAP_EVT_FIND_ADDR_COMPLETE, (tGAP_FINDADDR_RESULTS *) p_result);
+
+ /* Clear out the control block */
+ p_cb->in_use = FALSE;
+ p_cb->p_cback = (tGAP_CALLBACK *) NULL;
+ }
+}
+
+/*******************************************************************************
+**
+** Function gap_find_local_addr_by_name
+**
+** Description Searches through the internal inquiry database for a device
+** that has the same name as the one passed in. If found, the
+** device address is filled in.
+**
+** NOTE: It only searches up to the first BTM_MAX_REM_BD_NAME_LEN
+** bytes because the inquiry database uses tBTM_BD_NAME.
+**
+** Returns BT_PASS if the name was found and the device address is filled in
+** GAP_EOINQDB if the name was not found in the database
+** GAP_NO_DATA_AVAIL if the name is not saved with the inquiry
+**
+*******************************************************************************/
+UINT16 gap_find_local_addr_by_name (const tBTM_BD_NAME devname, BD_ADDR bd_addr)
+{
+
+/* If the remote name is retrieved automatically during an inquiry search the local db */
+#if (BTM_INQ_GET_REMOTE_NAME == TRUE)
+ tBTM_INQ_INFO *p_result;
+
+ p_result = BTM_InqDbFirst();
+
+ while (p_result)
+ {
+ /* Check the entry for a device name match */
+ if (!strncmp ((char *)devname, (char *)p_result->remote_name, BTM_MAX_REM_BD_NAME_LEN))
+ {
+ memcpy (bd_addr, p_result->results.remote_bd_addr, BD_ADDR_LEN);
+ return (BT_PASS);
+ }
+ else
+ p_result = BTM_InqDbNext(p_result);
+ };
+
+ return (GAP_EOINQDB);
+#else
+ UNUSED(devname);
+ UNUSED(bd_addr);
+ /* No data available because we are not automatically saving the data */
+ return (GAP_NO_DATA_AVAIL);
+#endif
+}
+#endif
+
+/*******************************************************************************
+**
+** Function gap_allocate_cb
+**
+** Description Look through the GAP Control Blocks for a free one.
+**
+** Returns Pointer to the control block or NULL if not found
+**
+*******************************************************************************/
+tGAP_INFO *gap_allocate_cb (void)
+{
+ tGAP_INFO *p_cb = &gap_cb.blk[0];
+ UINT8 x;
+
+ for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
+ {
+ if (!p_cb->in_use)
+ {
+ memset (p_cb, 0, sizeof (tGAP_INFO));
+
+ p_cb->in_use = TRUE;
+ p_cb->index = x;
+ p_cb->p_data = (void *)NULL;
+ return (p_cb);
+ }
+ }
+
+ /* If here, no free control blocks found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_free_cb
+**
+** Description Release GAP control block.
+**
+** Returns Pointer to the control block or NULL if not found
+**
+*******************************************************************************/
+void gap_free_cb (tGAP_INFO *p_cb)
+{
+ if (p_cb)
+ {
+ p_cb->gap_cback = NULL;
+ p_cb->in_use = FALSE;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function gap_is_service_busy
+**
+** Description Look through the GAP Control Blocks that are in use
+** and check to see if the event waiting for is the command
+** requested.
+**
+** Returns TRUE if already in use
+** FALSE if not busy
+**
+*******************************************************************************/
+BOOLEAN gap_is_service_busy (UINT16 request)
+{
+ tGAP_INFO *p_cb = &gap_cb.blk[0];
+ UINT8 x;
+
+ for (x = 0; x < GAP_MAX_BLOCKS; x++, p_cb++)
+ {
+ if (p_cb->in_use && p_cb->event == request)
+ return (TRUE);
+ }
+
+ /* If here, service is not busy */
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function gap_convert_btm_status
+**
+** Description Converts a BTM error status into a GAP error status
+**
+**
+** Returns GAP_UNKNOWN_BTM_STATUS is returned if not recognized
+**
+*******************************************************************************/
+UINT16 gap_convert_btm_status (tBTM_STATUS btm_status)
+{
+ switch (btm_status)
+ {
+ case BTM_SUCCESS:
+ return (BT_PASS);
+
+ case BTM_CMD_STARTED:
+ return (GAP_CMD_INITIATED);
+
+ case BTM_BUSY:
+ return (GAP_ERR_BUSY);
+
+ case BTM_MODE_UNSUPPORTED:
+ case BTM_ILLEGAL_VALUE:
+ return (GAP_ERR_ILL_PARM);
+
+ case BTM_WRONG_MODE:
+ return (GAP_DEVICE_NOT_UP);
+
+ case BTM_UNKNOWN_ADDR:
+ return (GAP_BAD_BD_ADDR);
+
+ case BTM_DEVICE_TIMEOUT:
+ return (GAP_ERR_TIMEOUT);
+
+ default:
+ return (GAP_ERR_PROCESSING);
+ }
+}
diff --git a/stack/gatt/gatt_main.c b/stack/gatt/gatt_main.c
index f0a0229ec..e1e9564f9 100644
--- a/stack/gatt/gatt_main.c
+++ b/stack/gatt/gatt_main.c
@@ -44,11 +44,13 @@
/********************************************************************************/
/* 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 gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport);
-static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf);
+static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected,
+ UINT16 reason, tBT_TRANSPORT transport);
+static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf);
static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congest);
-static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id);
+static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 l2cap_cid,
+ UINT16 psm, UINT8 l2cap_id);
static void gatt_l2cif_connect_cfm_cback (UINT16 l2cap_cid, UINT16 result);
static void gatt_l2cif_config_ind_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
static void gatt_l2cif_config_cfm_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
@@ -399,7 +401,7 @@ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, tBT_TRANSPORT trans
** connected (conn = TRUE)/disconnected (conn = FALSE).
**
*******************************************************************************/
-static void gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected,
+static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected,
UINT16 reason, tBT_TRANSPORT transport)
{
@@ -544,7 +546,7 @@ static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congested)
** Returns void
**
*******************************************************************************/
-static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf)
+static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf)
{
tGATT_TCB *p_tcb;
diff --git a/stack/include/gap_api.h b/stack/include/gap_api.h
index 0d86d0723..59c1294cb 100644
--- a/stack/include/gap_api.h
+++ b/stack/include/gap_api.h
@@ -19,9 +19,53 @@
#ifndef GAP_API_H
#define GAP_API_H
+#include "profiles_api.h"
+#include "btm_api.h"
+#include "l2c_api.h"
+
/*****************************************************************************
** Constants
*****************************************************************************/
+/*** GAP Error and Status Codes ***/
+#define GAP_UNSUPPORTED (GAP_ERR_GRP + 0x01) /* Unsupported call */
+#define GAP_EOINQDB (GAP_ERR_GRP + 0x02) /* End of inquiry database marker */
+#define GAP_ERR_BUSY (GAP_ERR_GRP + 0x03) /* The requested function was busy */
+#define GAP_ERR_NO_CTRL_BLK (GAP_ERR_GRP + 0x04) /* No control blocks available */
+#define GAP_ERR_STARTING_CMD (GAP_ERR_GRP + 0x05) /* Error occurred while initiating the command */
+#define GAP_NO_BDADDR_REC (GAP_ERR_GRP + 0x06) /* No Inquiry DB record for BD_ADDR */
+#define GAP_ERR_ILL_MODE (GAP_ERR_GRP + 0x07) /* An illegal mode parameter was detected */
+#define GAP_ERR_ILL_INQ_TIME (GAP_ERR_GRP + 0x08) /* An illegal time parameter was detected */
+#define GAP_ERR_ILL_PARM (GAP_ERR_GRP + 0x09) /* An illegal parameter was detected */
+#define GAP_ERR_REM_NAME (GAP_ERR_GRP + 0x0a) /* Error starting the remote device name request */
+#define GAP_CMD_INITIATED (GAP_ERR_GRP + 0x0b) /* The GAP command was started (result pending) */
+#define GAP_DEVICE_NOT_UP (GAP_ERR_GRP + 0x0c) /* The device was not up; the request was not executed */
+#define GAP_BAD_BD_ADDR (GAP_ERR_GRP + 0x0d) /* The bd addr passed in was not found or invalid */
+
+#define GAP_ERR_BAD_HANDLE (GAP_ERR_GRP + 0x0e) /* Bad GAP handle */
+#define GAP_ERR_BUF_OFFSET (GAP_ERR_GRP + 0x0f) /* Buffer offset invalid */
+#define GAP_ERR_BAD_STATE (GAP_ERR_GRP + 0x10) /* Connection is in invalid state */
+#define GAP_NO_DATA_AVAIL (GAP_ERR_GRP + 0x11) /* No data available */
+#define GAP_ERR_CONGESTED (GAP_ERR_GRP + 0x12) /* BT stack is congested */
+#define GAP_ERR_SECURITY (GAP_ERR_GRP + 0x13) /* Security failed */
+
+#define GAP_ERR_PROCESSING (GAP_ERR_GRP + 0x14) /* General error processing BTM request */
+#define GAP_ERR_TIMEOUT (GAP_ERR_GRP + 0x15) /* Timeout occurred while processing cmd */
+#define GAP_EVT_CONN_OPENED 0x0100
+#define GAP_EVT_CONN_CLOSED 0x0101
+#define GAP_EVT_CONN_DATA_AVAIL 0x0102
+#define GAP_EVT_CONN_CONGESTED 0x0103
+#define GAP_EVT_CONN_UNCONGESTED 0x0104
+/* Values for 'chan_mode_mask' field */
+/* GAP_ConnOpen() - optional channels to negotiate */
+#define GAP_FCR_CHAN_OPT_BASIC L2CAP_FCR_CHAN_OPT_BASIC
+#define GAP_FCR_CHAN_OPT_ERTM L2CAP_FCR_CHAN_OPT_ERTM
+#define GAP_FCR_CHAN_OPT_STREAM L2CAP_FCR_CHAN_OPT_STREAM
+/*** used in connection variables and functions ***/
+#define GAP_INVALID_HANDLE 0xFFFF
+
+/* This is used to change the criteria for AMP */
+#define GAP_PROTOCOL_ID (UUID_PROTOCOL_UDP)
+
#ifndef GAP_PREFER_CONN_INT_MAX
#define GAP_PREFER_CONN_INT_MAX BTM_BLE_CONN_INT_MIN
@@ -42,6 +86,25 @@
/*****************************************************************************
** Type Definitions
*****************************************************************************/
+/*
+** Callback function for connection services
+*/
+typedef void (tGAP_CONN_CALLBACK) (UINT16 gap_handle, UINT16 event);
+
+/*
+** Define the callback function prototypes. Parameters are specific
+** to each event and are described below
+*/
+typedef void (tGAP_CALLBACK) (UINT16 event, void *p_data);
+
+
+/* Definition of the GAP_FindAddrByName results structure */
+typedef struct
+{
+ UINT16 status;
+ BD_ADDR bd_addr;
+ tBTM_BD_NAME devname;
+} tGAP_FINDADDR_RESULTS;
typedef struct
{
@@ -69,6 +132,177 @@ typedef void (tGAP_BLE_RECONN_ADDR_CBACK)(BOOLEAN status, BD_ADDR addr, BD_ADDR
** External Function Declarations
*****************************************************************************/
+/*** Functions for L2CAP connection interface ***/
+
+/*******************************************************************************
+**
+** Function GAP_ConnOpen
+**
+** Description This function is called to open a generic L2CAP connection.
+**
+** Returns handle of the connection if successful, else GAP_INVALID_HANDLE
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnOpen (char *p_serv_name, UINT8 service_id, BOOLEAN is_server,
+ BD_ADDR p_rem_bda, UINT16 psm, tL2CAP_CFG_INFO *p_cfg,
+ tL2CAP_ERTM_INFO *ertm_info,
+ UINT16 security, UINT8 chan_mode_mask, tGAP_CONN_CALLBACK *p_cb);
+
+/*******************************************************************************
+**
+** Function GAP_ConnClose
+**
+** Description This function is called to close a connection.
+**
+** Returns BT_PASS - closed OK
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnClose (UINT16 gap_handle);
+
+/*******************************************************************************
+**
+** Function GAP_ConnReadData
+**
+** Description GKI buffer unaware application will call this function
+** after receiving GAP_EVT_RXDATA event. A data copy is made
+** into the receive buffer parameter.
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_NO_DATA_AVAIL - no data available
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnReadData (UINT16 gap_handle, UINT8 *p_data,
+ UINT16 max_len, UINT16 *p_len);
+
+/*******************************************************************************
+**
+** Function GAP_GetRxQueueCnt
+**
+** Description This function return number of bytes on the rx queue.
+**
+** Parameters: handle - Handle returned in the GAP_ConnOpen
+** p_rx_queue_count - Pointer to return queue count in.
+**
+**
+*******************************************************************************/
+extern int GAP_GetRxQueueCnt (UINT16 handle, UINT32 *p_rx_queue_count);
+
+/*******************************************************************************
+**
+** Function GAP_ConnBTRead
+**
+** Description GKI buffer aware applications will call this function after
+** receiving an GAP_EVT_RXDATA event to process the incoming
+** data buffer.
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_NO_DATA_AVAIL - no data available
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnBTRead (UINT16 gap_handle, BT_HDR **pp_buf);
+
+/*******************************************************************************
+**
+** Function GAP_ConnBTWrite
+**
+** Description GKI buffer aware applications can call this function to write data
+** by passing a pointer to the GKI buffer of data.
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_ERR_BAD_STATE - connection not established
+** GAP_INVALID_BUF_OFFSET - buffer offset is invalid
+*******************************************************************************/
+extern UINT16 GAP_ConnBTWrite (UINT16 gap_handle, BT_HDR *p_buf);
+
+/*******************************************************************************
+**
+** Function GAP_ConnWriteData
+**
+** Description GKI buffer unaware application will call this function
+** to send data to the connection. A data copy is made into a GKI
+** buffer.
+**
+** Returns BT_PASS - data read
+** GAP_ERR_BAD_HANDLE - invalid handle
+** GAP_ERR_BAD_STATE - connection not established
+** GAP_CONGESTION - system is congested
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnWriteData (UINT16 gap_handle, UINT8 *p_data,
+ UINT16 max_len, UINT16 *p_len);
+
+/*******************************************************************************
+**
+** Function GAP_ConnReconfig
+**
+** Description Applications can call this function to reconfigure the connection.
+**
+** Returns BT_PASS - config process started
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnReconfig (UINT16 gap_handle, tL2CAP_CFG_INFO *p_cfg);
+
+/*******************************************************************************
+**
+** Function GAP_ConnSetIdleTimeout
+**
+** Description Higher layers call this function to set the idle timeout for
+** a connection, or for all future connections. The "idle timeout"
+** is the amount of time that a connection can remain up with
+** no L2CAP channels on it. A timeout of zero means that the
+** connection will be torn down immediately when the last channel
+** is removed. A timeout of 0xFFFF means no timeout. Values are
+** in seconds.
+**
+** Returns BT_PASS - config process started
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnSetIdleTimeout (UINT16 gap_handle, UINT16 timeout);
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetRemoteAddr
+**
+** Description This function is called to get the remote BD address
+** of a connection.
+**
+** Returns BT_PASS - closed OK
+** GAP_ERR_BAD_HANDLE - invalid handle
+**
+*******************************************************************************/
+extern UINT8 *GAP_ConnGetRemoteAddr (UINT16 gap_handle);
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetRemMtuSize
+**
+** Description Returns the remote device's MTU size.
+**
+** Returns UINT16 - maximum size buffer that can be transmitted to the peer
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnGetRemMtuSize (UINT16 gap_handle);
+
+/*******************************************************************************
+**
+** Function GAP_ConnGetL2CAPCid
+**
+** Description Returns the L2CAP channel id
+**
+** Parameters: handle - Handle of the connection
+**
+** Returns UINT16 - The L2CAP channel id
+** 0, if error
+**
+*******************************************************************************/
+extern UINT16 GAP_ConnGetL2CAPCid (UINT16 gap_handle);
+
/*******************************************************************************
**
** Function GAP_SetTraceLevel
diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h
index 010161428..c89efdd76 100644
--- a/stack/include/l2c_api.h
+++ b/stack/include/l2c_api.h
@@ -931,18 +931,20 @@ extern BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY pri
*******************************************************************************/
/* Fixed channel connected and disconnected. Parameters are
+** channel
** BD Address of remote
** TRUE if channel is connected, FALSE if disconnected
** Reason for connection failure
** transport : physical transport, BR/EDR or LE
*/
-typedef void (tL2CA_FIXED_CHNL_CB) (BD_ADDR, BOOLEAN, UINT16, tBT_TRANSPORT);
+typedef void (tL2CA_FIXED_CHNL_CB) (UINT16, BD_ADDR, BOOLEAN, UINT16, tBT_TRANSPORT);
/* Signalling data received. Parameters are
+** channel
** BD Address of remote
** Pointer to buffer with data
*/
-typedef void (tL2CA_FIXED_DATA_CB) (BD_ADDR, BT_HDR *);
+typedef void (tL2CA_FIXED_DATA_CB) (UINT16, BD_ADDR, BT_HDR *);
/* Congestion status callback protype. This callback is optional. If
** an application tries to send data when the transmit queue is full,
diff --git a/stack/include/l2cdefs.h b/stack/include/l2cdefs.h
index 1063749dd..86044bf9a 100644
--- a/stack/include/l2cdefs.h
+++ b/stack/include/l2cdefs.h
@@ -253,6 +253,11 @@
#define L2CAP_EXT_CONTROL_OVERHEAD 4 /* Extended Control Field */
#define L2CAP_MAX_HEADER_FCS (L2CAP_PKT_OVERHEAD + L2CAP_EXT_CONTROL_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN)
/* length(2), channel(2), control(4), SDU length(2) FCS(2) */
+/* To optimize this, it must be a multiplum of the L2CAP PDU length AND match the 3DH5 air
+ * including the l2cap headers in each packet - to match the latter - the -5 is added
+ */
+#define L2CAP_MAX_SDU_LENGTH (GKI_BUF4_SIZE - (L2CAP_MIN_OFFSET + L2CAP_MAX_HEADER_FCS) -5)
+
/* Part of L2CAP_MIN_OFFSET that is not part of L2CAP
*/
#define L2CAP_OFFSET_WO_L2HDR (L2CAP_MIN_OFFSET-(L2CAP_PKT_OVERHEAD+L2CAP_FCR_OVERHEAD))
diff --git a/stack/include/sdpdefs.h b/stack/include/sdpdefs.h
index 22d87c1ad..44d87e74b 100644
--- a/stack/include/sdpdefs.h
+++ b/stack/include/sdpdefs.h
@@ -64,13 +64,14 @@
#define ATTR_ID_IP_SUBNET 0x0200 /* PAN Profile (***) */
#define ATTR_ID_VERSION_NUMBER_LIST 0x0200
+#define ATTR_ID_GOEP_L2CAP_PSM 0x0200
#define ATTR_ID_GROUP_ID 0x0200
#define ATTR_ID_SERVICE_DATABASE_STATE 0x0201
#define ATTR_ID_SERVICE_VERSION 0x0300
#define ATTR_ID_HCRP_1284ID 0x0300
#define ATTR_ID_SUPPORTED_DATA_STORES 0x0301
-#define ATTR_ID_NETWORK 0x0301
+#define ATTR_ID_NETWORK 0x0301
#define ATTR_ID_EXTERNAL_NETWORK 0x0301
#define ATTR_ID_FAX_CLASS_1_SUPPORT 0x0302
#define ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL 0x0302
@@ -92,6 +93,9 @@
#define ATTR_ID_SUPPORTED_REPOSITORIES 0x0314 /* Phone book access Profile */
#define ATTR_ID_MAS_INSTANCE_ID 0x0315 /* MAP profile */
#define ATTR_ID_SUPPORTED_MSG_TYPE 0x0316 /* MAP profile */
+#define ATTR_ID_MAP_SUPPORTED_FEATURES 0x0317 /* MAP profile */
+#define ATTR_ID_PBAP_SUPPORTED_FEATURES 0x0317 /* PBAP profile */
+
/* These values are for the BPP profile */
#define ATTR_ID_DOCUMENT_FORMATS_SUPPORTED 0x0350
diff --git a/stack/l2cap/l2c_api.c b/stack/l2cap/l2c_api.c
index 959f4d254..839ad1b5e 100644
--- a/stack/l2cap/l2c_api.c
+++ b/stack/l2cap/l2c_api.c
@@ -1445,10 +1445,10 @@ BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
#if BLE_INCLUDED == TRUE
(*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
- (p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);
+ (fixed_cid,p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);
#else
(*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
- (p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
+ (fixed_cid, p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
#endif
return TRUE;
}
diff --git a/stack/l2cap/l2c_ble.c b/stack/l2cap/l2c_ble.c
index 65ac7ce08..a2b0a75c9 100644
--- a/stack/l2cap/l2c_ble.c
+++ b/stack/l2cap/l2c_ble.c
@@ -264,6 +264,7 @@ void l2cble_notify_le_connection (BD_ADDR bda)
void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
{
+ int i;
tL2C_LCB *p_lcb;
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda);
@@ -352,6 +353,14 @@ void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
btm_ble_set_conn_st(BLE_CONN_IDLE);
+ /*
+ * This is wrong. However, querying the other side is wrong too, since as per spec they
+ * cannot really tell us when they have fixed channels open. Yes, bluedroid breaks the
+ * spec in EDR mode in that respect, but that it a whole new story.
+ */
+ for(i = 0; i < L2CAP_FIXED_CHNL_ARRAY_SIZE; i++)
+ p_lcb->peer_chnl_mask[i] = 0xFF;
+ l2cu_process_fixed_chnl_resp (p_lcb);
}
@@ -368,6 +377,7 @@ void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
{
+ int i;
tL2C_LCB *p_lcb;
tBTM_SEC_DEV_REC *p_dev_rec;
UNUSED(type);
@@ -430,6 +440,14 @@ void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE typ
{
L2CA_CancelBleConnectReq(bda);
}
+ /*
+ * This is wrong. However, querying the other side is wrong too, since as per spec they
+ * cannot really tell us when they have fixed channels open. Yes, bluedroid breaks the
+ * spec in EDR mode in that respect, but that it a whole new story.
+ */
+ for(i = 0; i < L2CAP_FIXED_CHNL_ARRAY_SIZE; i++)
+ p_lcb->peer_chnl_mask[i] = 0xFF;
+ l2cu_process_fixed_chnl_resp (p_lcb);
}
/*******************************************************************************
diff --git a/stack/l2cap/l2c_csm.c b/stack/l2cap/l2c_csm.c
index 325f72039..97372ba4d 100644
--- a/stack/l2cap/l2c_csm.c
+++ b/stack/l2cap/l2c_csm.c
@@ -103,6 +103,7 @@ void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
break;
default:
+ L2CAP_TRACE_DEBUG("Unhandled event! event = %d",event);
break;
}
}
@@ -890,7 +891,8 @@ static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
if (p_ccb->local_cid < L2CAP_BASE_APPL_CID)
{
if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
- (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_ccb->p_lcb->remote_bd_addr,(BT_HDR *)p_data);
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr,(BT_HDR *)p_data);
else
GKI_freebuf (p_data);
break;
@@ -909,7 +911,8 @@ static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
case L2CEVT_TIMEOUT:
l2cu_send_peer_disc_req (p_ccb);
- L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
l2cu_release_ccb (p_ccb);
(*disconnect_ind)(local_cid, FALSE);
break;
@@ -936,7 +939,8 @@ static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
UINT8 cfg_result;
#if (BT_TRACE_VERBOSE == TRUE)
- L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: OPEN evt: %s",
+ p_ccb->local_cid, l2c_csm_get_event_name (event));
#else
L2CAP_TRACE_EVENT ("L2CAP - st: OPEN evt: %d", event);
#endif
@@ -956,7 +960,8 @@ static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
switch (event)
{
case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
- L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
l2cu_release_ccb (p_ccb);
if (p_ccb->p_rcb)
(*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
diff --git a/stack/l2cap/l2c_fcr.c b/stack/l2cap/l2c_fcr.c
index ec9a4ef88..3df5cba7e 100644
--- a/stack/l2cap/l2c_fcr.c
+++ b/stack/l2cap/l2c_fcr.c
@@ -1477,7 +1477,8 @@ static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_wo
(p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL && p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL))
{
if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
- (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_ccb->p_lcb->remote_bd_addr, p_buf);
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, p_buf);
}
else
#endif
diff --git a/stack/l2cap/l2c_link.c b/stack/l2cap/l2c_link.c
index 615e9a705..836f4ba54 100644
--- a/stack/l2cap/l2c_link.c
+++ b/stack/l2cap/l2c_link.c
@@ -442,11 +442,11 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
if (p_lcb->p_fixed_ccbs[xx] && p_lcb->p_fixed_ccbs[xx] != p_lcb->p_pending_ccb)
{
#if BLE_INCLUDED == TRUE
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE,
- p_lcb->disc_reason, p_lcb->transport);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
#else
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE,
- p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
#endif
l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
diff --git a/stack/l2cap/l2c_main.c b/stack/l2cap/l2c_main.c
index 5f541b5e3..ef8f4bc83 100644
--- a/stack/l2cap/l2c_main.c
+++ b/stack/l2cap/l2c_main.c
@@ -274,7 +274,8 @@ void l2c_rcv_acl_data (BT_HDR *p_msg)
if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)
l2c_fcr_proc_pdu (p_ccb, p_msg);
else
- (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_lcb->remote_bd_addr, p_msg);
+ (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (rcv_cid, p_lcb->remote_bd_addr, p_msg);
}
else
GKI_freebuf (p_msg);
diff --git a/stack/l2cap/l2c_utils.c b/stack/l2cap/l2c_utils.c
index ef155ff8b..00ff2e7b5 100644
--- a/stack/l2cap/l2c_utils.c
+++ b/stack/l2cap/l2c_utils.c
@@ -1216,7 +1216,7 @@ void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 info_type
for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL)
- p[0] |= 1 << (xx + L2CAP_FIRST_FIXED_CHNL);
+ p[(xx + L2CAP_FIRST_FIXED_CHNL) / 8] |= 1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8);
}
#endif
}
@@ -2836,22 +2836,27 @@ void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb)
#endif
if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL)
{
- if (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
+ if (p_lcb->peer_chnl_mask[(xx + L2CAP_FIRST_FIXED_CHNL) / 8]
+ & (1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8)))
{
if (p_lcb->p_fixed_ccbs[xx])
p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN;
#if BLE_INCLUDED == TRUE
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);
#else
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
#endif
}
else
{
#if BLE_INCLUDED == TRUE
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
#else
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
#endif
if (p_lcb->p_fixed_ccbs[xx])
@@ -2898,18 +2903,22 @@ void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb)
p_lcb->p_fixed_ccbs[xx] = NULL;
l2cu_release_ccb(p_l2c_chnl_ctrl_block);
#if BLE_INCLUDED == TRUE
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
#else
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
#endif
}
}
else if ( (peer_channel_mask & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
&& (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) )
#if BLE_INCLUDED == TRUE
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
#else
- (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
#endif
}
#endif
@@ -2926,7 +2935,8 @@ void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb)
** Returns void
**
*******************************************************************************/
-void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout)
+void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int,
+ UINT16 latency, UINT16 timeout)
{
BT_HDR *p_buf;
UINT8 *p;
@@ -2935,7 +2945,8 @@ void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int
p_lcb->id++;
l2cu_adj_id (p_lcb, L2CAP_ADJ_ID);
- if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_REQ_LEN, L2CAP_CMD_BLE_UPDATE_REQ, p_lcb->id)) == NULL )
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_REQ_LEN,
+ L2CAP_CMD_BLE_UPDATE_REQ, p_lcb->id)) == NULL )
{
L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_req - no buffer");
return;
@@ -2967,7 +2978,8 @@ void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id)
BT_HDR *p_buf;
UINT8 *p;
- if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_RSP_LEN, L2CAP_CMD_BLE_UPDATE_RSP, rem_id)) == NULL )
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_RSP_LEN,
+ L2CAP_CMD_BLE_UPDATE_RSP, rem_id)) == NULL )
{
L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_rsp - no buffer");
return;
diff --git a/stack/smp/smp_l2c.c b/stack/smp/smp_l2c.c
index 63c851c79..3c91cc08e 100644
--- a/stack/smp/smp_l2c.c
+++ b/stack/smp/smp_l2c.c
@@ -34,8 +34,9 @@
-static void smp_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport);
-static void smp_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf);
+static void smp_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected,
+ UINT16 reason, tBT_TRANSPORT transport);
+static void smp_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf);
/*******************************************************************************
**
@@ -74,7 +75,7 @@ void smp_l2cap_if_init (void)
** connected (conn = TRUE)/disconnected (conn = FALSE).
**
*******************************************************************************/
-static void smp_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason,
+static void smp_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason,
tBT_TRANSPORT transport)
{
tSMP_CB *p_cb = &smp_cb;
@@ -129,7 +130,7 @@ static void smp_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason
** Returns void
**
*******************************************************************************/
-static void smp_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf)
+static void smp_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf)
{
tSMP_CB *p_cb = &smp_cb;
UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;