diff options
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, ¶m); + min_pri = sched_get_priority_min(policy); + if (param.sched_priority > min_pri) { + param.sched_priority -= 1; + } + pthread_setschedparam(*thread_id, policy, ¶m); + 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; |