diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
| commit | ee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch) | |
| tree | 8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/gap | |
| parent | 239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff) | |
| download | tangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz | |
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/host/bluedroid/stack/gap')
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gap/gap_api.c | 109 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gap/gap_ble.c | 823 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gap/gap_conn.c | 1211 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gap/gap_utils.c | 141 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gap/include/gap_int.h | 159 |
5 files changed, 2443 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/gap/gap_api.c b/lib/bt/host/bluedroid/stack/gap/gap_api.c new file mode 100644 index 00000000..75d5b5d1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_api.c @@ -0,0 +1,109 @@ +/****************************************************************************** + * + * 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 "common/bt_target.h" +//#include "bt_utils.h" +#include "gap_int.h" +#include "osi/allocator.h" + +#if GAP_DYNAMIC_MEMORY == FALSE +tGAP_CB gap_cb; +#else +tGAP_CB *gap_cb_ptr; +#endif + +/******************************************************************************* +** +** Function GAP_SetTraceLevel +** +** Description This function sets the trace level for GAP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 GAP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + gap_cb.trace_level = new_level; + } + + return (gap_cb.trace_level); +} + +/******************************************************************************* +** +** Function GAP_Init +** +** Description Initializes the control blocks used by GAP. +** +** This routine should not be called except once per +** stack invocation. +** +** Returns status +** +*******************************************************************************/ +bt_status_t GAP_Init(void) +{ +#if GAP_DYNAMIC_MEMORY == TRUE + gap_cb_ptr = (tGAP_CB *)osi_malloc(sizeof(tGAP_CB)); + if (!gap_cb_ptr) { + return BT_STATUS_NOMEM; + } +#endif + + memset (&gap_cb, 0, sizeof (tGAP_CB)); + +#if defined(GAP_INITIAL_TRACE_LEVEL) + gap_cb.trace_level = GAP_INITIAL_TRACE_LEVEL; +#else + gap_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + +#if GAP_CONN_INCLUDED == TRUE + gap_conn_init(); +#endif + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + gap_attr_db_init(); +#endif + + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* +** +** Function GAP_Deinit +** +** Description This function is called to deinitialize the control block +** for this layer. +** +** Returns void +** +*******************************************************************************/ +void GAP_Deinit(void) +{ +#if GAP_DYNAMIC_MEMORY == TRUE + if (gap_cb_ptr) { + osi_free(gap_cb_ptr); + gap_cb_ptr = NULL; + } +#endif +} diff --git a/lib/bt/host/bluedroid/stack/gap/gap_ble.c b/lib/bt/host/bluedroid/stack/gap/gap_ble.c new file mode 100644 index 00000000..5bac86f0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_ble.c @@ -0,0 +1,823 @@ +/****************************************************************************** + * + * 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 "common/bt_target.h" + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +#include "common/bt_defs.h" +#include "osi/allocator.h" +#include <string.h> +#include "gap_int.h" +#include "stack/gap_api.h" +#include "stack/gattdefs.h" +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "btm_int.h" +#include "stack/hcimsgs.h" +#include "stack/sdpdefs.h" + +#define GAP_CHAR_ICON_SIZE 2 +#define GAP_CHAR_DEV_NAME_SIZE 248 +#define GAP_MAX_NUM_INC_SVR 0 +#define GAP_MAX_ATTR_NUM (2 * GAP_MAX_CHAR_NUM + GAP_MAX_NUM_INC_SVR + 1) +#define GAP_MAX_CHAR_VALUE_SIZE (30 + GAP_CHAR_DEV_NAME_SIZE) + + +#ifndef GAP_ATTR_DB_SIZE +#define GAP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GAP_MAX_NUM_INC_SVR, GAP_MAX_CHAR_NUM, GAP_MAX_CHAR_VALUE_SIZE) +#endif + +static void gap_ble_s_attr_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE op_code, tGATTS_DATA *p_data); + +/* client connection callback */ +static void gap_ble_c_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, + tGATT_DISCONN_REASON reason, tGATT_TRANSPORT transport); +static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + +static const tGATT_CBACK gap_cback = { + gap_ble_c_connect_cback, + gap_ble_c_cmpl_cback, + NULL, + NULL, + gap_ble_s_attr_request_cback, + NULL, + NULL +}; + + + +/******************************************************************************* +** +** Function gap_find_clcb_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGAP_CLCB *gap_find_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (p_clcb->in_use && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gap_ble_find_clcb_by_conn_id +** +** Description The function searches all LCB with macthing connection ID +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGAP_CLCB *gap_ble_find_clcb_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) { + return p_clcb; + } + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gap_clcb_alloc +** +** Description The function allocates a GAP connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGAP_CLCB *gap_clcb_alloc (BD_ADDR bda) +{ + UINT8 i_clcb = 0; + tGAP_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gap_cb.clcb; i_clcb < GAP_MAX_CL; i_clcb++, p_clcb++) { + if (!p_clcb->in_use) { + memset(p_clcb, 0, sizeof(tGAP_CLCB)); + p_clcb->in_use = TRUE; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function gap_ble_dealloc_clcb +** +** Description The function clean up the pending request queue in GAP +** +** Returns none +** +*******************************************************************************/ +void gap_ble_dealloc_clcb(tGAP_CLCB *p_clcb) +{ + tGAP_BLE_REQ *p_q; + + while ((p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0)) != NULL) { + /* send callback to all pending requests if being removed*/ + if (p_q->p_cback != NULL) { + (*p_q->p_cback)(FALSE, p_clcb->bda, 0, NULL); + } + + osi_free (p_q); + } + + memset(p_clcb, 0, sizeof(tGAP_CLCB)); +} + +/******************************************************************************* +** +** Function gap_ble_enqueue_request +** +** Description The function enqueue a GAP client request +** +** Returns TRUE is successul; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN gap_ble_enqueue_request (tGAP_CLCB *p_clcb, UINT16 uuid, tGAP_BLE_CMPL_CBACK *p_cback) +{ + tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)osi_malloc(sizeof(tGAP_BLE_REQ)); + + if (p_q != NULL) { + p_q->p_cback = p_cback; + p_q->uuid = uuid; + fixed_queue_enqueue(p_clcb->pending_req_q, p_q, FIXED_QUEUE_MAX_TIMEOUT); + return TRUE; + } + + return FALSE; +} +/******************************************************************************* +** +** Function gap_ble_dequeue_request +** +** Description The function dequeue a GAP client request if any +** +** Returns TRUE is successul; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN gap_ble_dequeue_request (tGAP_CLCB *p_clcb, UINT16 *p_uuid, tGAP_BLE_CMPL_CBACK **p_cback) +{ + tGAP_BLE_REQ *p_q = (tGAP_BLE_REQ *)fixed_queue_dequeue(p_clcb->pending_req_q, 0);; + + if (p_q != NULL) { + *p_cback = p_q->p_cback; + *p_uuid = p_q->uuid; + osi_free((void *)p_q); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** GAP Attributes Database Request callback +*******************************************************************************/ +tGATT_STATUS gap_read_attr_value (UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 *p = p_value->value, i; + UINT16 offset = p_value->offset; + UINT8 *p_dev_name = NULL; + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (handle == p_db_attr->handle) { + if (p_db_attr->uuid != GATT_UUID_GAP_DEVICE_NAME && + is_long == TRUE) { + return GATT_NOT_LONG; + } + + switch (p_db_attr->uuid) { + case GATT_UUID_GAP_DEVICE_NAME: + BTM_ReadLocalDeviceName((char **)&p_dev_name); + if (strlen ((char *)p_dev_name) > GATT_MAX_ATTR_LEN) { + p_value->len = GATT_MAX_ATTR_LEN; + } else { + p_value->len = (UINT16)strlen ((char *)p_dev_name); + } + + if (offset > p_value->len) { + return GATT_INVALID_OFFSET; + } else { + p_value->len -= offset; + p_dev_name += offset; + ARRAY_TO_STREAM(p, p_dev_name, p_value->len); + GAP_TRACE_EVENT("GATT_UUID_GAP_DEVICE_NAME len=0x%04x", p_value->len); + } + break; + + case GATT_UUID_GAP_ICON: + UINT16_TO_STREAM(p, p_db_attr->attr_value.icon); + p_value->len = 2; + break; + + case GATT_UUID_GAP_PREF_CONN_PARAM: + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.int_min); /* int_min */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.int_max); /* int_max */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.latency); /* latency */ + UINT16_TO_STREAM(p, p_db_attr->attr_value.conn_param.sp_tout); /* sp_tout */ + p_value->len = 8; + break; + + /* address resolution */ + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + UINT8_TO_STREAM(p, p_db_attr->attr_value.addr_resolution); + p_value->len = 1; + break; + } + return GATT_SUCCESS; + } + } + return GATT_NOT_FOUND; +} + +/******************************************************************************* +** GAP Attributes Database Read/Read Blob Request process +*******************************************************************************/ +tGATT_STATUS gap_proc_read (tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + UNUSED(type); + + if (p_data->is_long) { + p_rsp->attr_value.offset = p_data->offset; + } + + p_rsp->attr_value.handle = p_data->handle; + + status = gap_read_attr_value(p_data->handle, &p_rsp->attr_value, p_data->is_long); + + return status; +} + +/****************************************************************************** +** +** Function gap_proc_write_req +** +** Description GAP ATT server process a write request. +** +** Returns void. +** +*******************************************************************************/ +UINT8 gap_proc_write_req( tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 i; + UNUSED(type); + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (p_data->handle == p_db_attr->handle) { + switch (p_db_attr->uuid) { + #if (GATTS_DEVICE_NAME_WRITABLE == TRUE) + case GATT_UUID_GAP_DEVICE_NAME: { + UINT8 *p_val = p_data->value; + p_val[p_data->len] = '\0'; + BTM_SetLocalDeviceName((char *)p_val); + return GATT_SUCCESS; + } + #endif + #if (GATTS_APPEARANCE_WRITABLE == TRUE) + case GATT_UUID_GAP_ICON: { + UINT8 *p_val = p_data->value; + if (p_data->len != sizeof(UINT16)) { + return GATT_INVALID_ATTR_LEN; + } + STREAM_TO_UINT16(p_db_attr->attr_value.icon, p_val); + return GATT_SUCCESS; + } + #endif + default: + break; + } + return GATT_WRITE_NOT_PERMIT; + } + } + + return GATT_NOT_FOUND; +} + +/****************************************************************************** +** +** Function gap_ble_s_attr_request_cback +** +** Description GAP ATT server attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +void gap_ble_s_attr_request_cback (UINT16 conn_id, UINT32 trans_id, + tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + UINT8 status = GATT_INVALID_PDU; + tGATTS_RSP rsp_msg; + BOOLEAN ignore = FALSE; + + GAP_TRACE_EVENT("gap_ble_s_attr_request_cback : recv type (0x%02x)", type); + + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + switch (type) { + case GATTS_REQ_TYPE_READ: + status = gap_proc_read(type, &p_data->read_req, &rsp_msg); + break; + + case GATTS_REQ_TYPE_WRITE: + if (!p_data->write_req.need_rsp) { + ignore = TRUE; + } + + status = gap_proc_write_req(type, &p_data->write_req); + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + ignore = TRUE; + GAP_TRACE_EVENT("Ignore GATTS_REQ_TYPE_WRITE_EXEC" ); + break; + + case GATTS_REQ_TYPE_MTU: + GAP_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); + ignore = TRUE; + break; + + default: + GAP_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + if (!ignore) { + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + } +} + +/******************************************************************************* +** +** Function btm_ble_att_db_init +** +** Description GAP ATT database initalization. +** +** Returns void. +** +*******************************************************************************/ +void gap_attr_db_init(void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GAP_SERVER}}; + UINT16 service_handle; + tGAP_ATTR *p_db_attr = &gap_cb.gap_attr[0]; + tGATT_STATUS status; + + /* Fill our internal UUID with a fixed pattern 0x82 */ + memset (&app_uuid.uu.uuid128, 0x82, LEN_UUID_128); + memset(gap_cb.gap_attr, 0, sizeof(tGAP_ATTR) *GAP_MAX_CHAR_NUM); + + gap_cb.gatt_if = GATT_Register(&app_uuid, &gap_cback); + + GATT_StartIf(gap_cb.gatt_if); + + /* Create a GAP service */ + service_handle = GATTS_CreateService (gap_cb.gatt_if, &uuid, 0, GAP_MAX_ATTR_NUM, TRUE); + + GAP_TRACE_EVENT ("gap_attr_db_init service_handle = %d", service_handle); + + /* add Device Name Characteristic + */ + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_DEVICE_NAME; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + #if (GATTS_DEVICE_NAME_WRITABLE == TRUE) + GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR, + #else + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + #endif + NULL, NULL); + p_db_attr ++; + + /* add Icon characteristic + */ + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_ICON; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + #if (GATTS_APPEARANCE_WRITABLE == TRUE) + GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR, + #else + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + #endif + NULL, NULL); + p_db_attr ++; + +#if ((defined BTM_PERIPHERAL_ENABLED) && (BTM_PERIPHERAL_ENABLED == TRUE)) + /* Only needed for peripheral testing */ + /* add preferred connection parameter characteristic + */ + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_PREF_CONN_PARAM; + p_db_attr->attr_value.conn_param.int_max = GAP_PREFER_CONN_INT_MAX; /* 6 */ + p_db_attr->attr_value.conn_param.int_min = GAP_PREFER_CONN_INT_MIN; /* 0 */ + p_db_attr->attr_value.conn_param.latency = GAP_PREFER_CONN_LATENCY; /* 0 */ + p_db_attr->attr_value.conn_param.sp_tout = GAP_PREFER_CONN_SP_TOUT; /* 2000 */ + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, + &uuid, + GATT_PERM_READ, + GATT_CHAR_PROP_BIT_READ, + NULL, NULL); + p_db_attr ++; +#endif + + /* add Central address resolution Characteristic */ + uuid.len = LEN_UUID_16; + uuid.uu.uuid16 = p_db_attr->uuid = GATT_UUID_GAP_CENTRAL_ADDR_RESOL; + p_db_attr->handle = GATTS_AddCharacteristic(service_handle, &uuid, + GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, + NULL, NULL); + p_db_attr->attr_value.addr_resolution = 0; + p_db_attr++; + + /* start service now */ + memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); + + status = GATTS_StartService(gap_cb.gatt_if, service_handle, GAP_TRANSPORT_SUPPORTED ); +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GAP_TRACE_EVENT ("GAP App gatt_if: %d s_hdl = %d start_status=%d", + gap_cb.gatt_if, service_handle, status); + + + +} + +/******************************************************************************* +** +** Function GAP_BleAttrDBUpdate +** +** Description GAP ATT database update. +** +** Returns void. +** +*******************************************************************************/ +void GAP_BleAttrDBUpdate(UINT16 attr_uuid, tGAP_BLE_ATTR_VALUE *p_value) +{ + tGAP_ATTR *p_db_attr = gap_cb.gap_attr; + UINT8 i = 0; + + GAP_TRACE_EVENT("GAP_BleAttrDBUpdate attr_uuid=0x%04x\n", attr_uuid); + + for (i = 0; i < GAP_MAX_CHAR_NUM; i ++, p_db_attr ++) { + if (p_db_attr->uuid == attr_uuid) { + GAP_TRACE_EVENT("Found attr_uuid=0x%04x\n", attr_uuid); + + switch (attr_uuid) { + case GATT_UUID_GAP_ICON: + p_db_attr->attr_value.icon = p_value->icon; + break; + + case GATT_UUID_GAP_PREF_CONN_PARAM: + memcpy((void *)&p_db_attr->attr_value.conn_param, + (const void *)&p_value->conn_param, sizeof(tGAP_BLE_PREF_PARAM)); + break; + + case GATT_UUID_GAP_DEVICE_NAME: + BTM_SetLocalDeviceName((char *)p_value->p_dev_name); + break; + + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + p_db_attr->attr_value.addr_resolution = p_value->addr_resolution; + break; + + } + break; + } + } + + return; +} + +/******************************************************************************* +** +** Function gap_ble_send_cl_read_request +** +** Description utility function to send a read request for a GAP charactersitic +** +** Returns TRUE if read started, else FALSE if GAP is busy +** +*******************************************************************************/ +BOOLEAN gap_ble_send_cl_read_request(tGAP_CLCB *p_clcb) +{ + tGATT_READ_PARAM param; + UINT16 uuid = 0; + BOOLEAN started = FALSE; + + if (gap_ble_dequeue_request(p_clcb, &uuid, &p_clcb->p_cback)) { + memset(¶m, 0, sizeof(tGATT_READ_PARAM)); + + param.service.uuid.len = LEN_UUID_16; + param.service.uuid.uu.uuid16 = uuid; + param.service.s_handle = 1; + param.service.e_handle = 0xFFFF; + param.service.auth_req = 0; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Read(p_clcb->conn_id, GATT_READ_BY_TYPE, ¶m) == GATT_SUCCESS) { + p_clcb->cl_op_uuid = uuid; + started = TRUE; + } +#endif ///GATTC_INCLUDED == TRUE + } + + return started; +} + +/******************************************************************************* +** +** Function gap_ble_cl_op_cmpl +** +** Description GAP client operation complete callback +** +** Returns void +** +*******************************************************************************/ +void gap_ble_cl_op_cmpl(tGAP_CLCB *p_clcb, BOOLEAN status, UINT16 len, UINT8 *p_name) +{ + tGAP_BLE_CMPL_CBACK *p_cback = p_clcb->p_cback; + UINT16 op = p_clcb->cl_op_uuid; + + GAP_TRACE_EVENT("gap_ble_cl_op_cmpl status: %d", status); + + p_clcb->cl_op_uuid = 0; + p_clcb->p_cback = NULL; + + if (p_cback && op) { + GAP_TRACE_EVENT("calling gap_ble_cl_op_cmpl"); + (* p_cback)(status, p_clcb->bda, len, (char *)p_name); + } + + /* if no further activity is requested in callback, drop the link */ + if (p_clcb->connected) { + if (!gap_ble_send_cl_read_request(p_clcb)) { + GATT_Disconnect(p_clcb->conn_id); + gap_ble_dealloc_clcb(p_clcb); + } + } +} + +/******************************************************************************* +** +** Function gap_ble_c_connect_cback +** +** Description Client connection callback. +** +** Returns void +** +*******************************************************************************/ +static void gap_ble_c_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tGATT_TRANSPORT transport) +{ + tGAP_CLCB *p_clcb = gap_find_clcb_by_bd_addr (bda); + + UNUSED(gatt_if); + UNUSED(transport); + + if (p_clcb != NULL) { + if (connected) { + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + /* start operation is pending */ + gap_ble_send_cl_read_request(p_clcb); + } else { + p_clcb->connected = FALSE; + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + /* clean up clcb */ + gap_ble_dealloc_clcb(p_clcb); + } + } +} + +/******************************************************************************* +** +** Function gap_ble_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) + +{ + tGAP_CLCB *p_clcb = gap_ble_find_clcb_by_conn_id(conn_id); + UINT16 op_type; + UINT16 min, max, latency, tout; + UINT16 len; + UINT8 *pp; + + if (p_clcb == NULL) { + return; + } + + op_type = p_clcb->cl_op_uuid; + + GAP_TRACE_EVENT ("gap_ble_c_cmpl_cback() - op_code: 0x%02x status: 0x%02x read_type: 0x%04x\n", op, status, op_type); + /* Currently we only issue read commands */ + if (op != GATTC_OPTYPE_READ) { + return; + } + + if (status != GATT_SUCCESS) { + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + return; + } + + pp = p_data->att_value.value; + + switch (op_type) { + case GATT_UUID_GAP_PREF_CONN_PARAM: + GAP_TRACE_EVENT ("GATT_UUID_GAP_PREF_CONN_PARAM"); + /* Extract the peripheral preferred connection parameters and save them */ + + STREAM_TO_UINT16 (min, pp); + STREAM_TO_UINT16 (max, pp); + STREAM_TO_UINT16 (latency, pp); + STREAM_TO_UINT16 (tout, pp); + + BTM_BleSetPrefConnParams (p_clcb->bda, min, max, latency, tout); + /* release the connection here */ + gap_ble_cl_op_cmpl(p_clcb, TRUE, 0, NULL); + break; + + case GATT_UUID_GAP_DEVICE_NAME: + GAP_TRACE_EVENT ("GATT_UUID_GAP_DEVICE_NAME\n"); + len = (UINT16)strlen((char *)pp); + if (len > GAP_CHAR_DEV_NAME_SIZE) { + len = GAP_CHAR_DEV_NAME_SIZE; + } + gap_ble_cl_op_cmpl(p_clcb, TRUE, len, pp); + break; + + case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + gap_ble_cl_op_cmpl(p_clcb, TRUE, 1, pp); + break; + } +} + + +/******************************************************************************* +** +** Function gap_ble_accept_cl_operation +** +** Description Start a process to read peer address resolution capability +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN gap_ble_accept_cl_operation(BD_ADDR peer_bda, UINT16 uuid, tGAP_BLE_CMPL_CBACK *p_cback) +{ + tGAP_CLCB *p_clcb; + BOOLEAN started = FALSE; + + if (p_cback == NULL && uuid != GATT_UUID_GAP_PREF_CONN_PARAM) { + return (started); + } + + if ((p_clcb = gap_find_clcb_by_bd_addr (peer_bda)) == NULL) { + if ((p_clcb = gap_clcb_alloc(peer_bda)) == NULL) { + GAP_TRACE_ERROR("gap_ble_accept_cl_operation max connection reached"); + return started; + } + } + + GAP_TRACE_EVENT ("%s() - BDA: %08x%04x cl_op_uuid: 0x%04x", + __FUNCTION__, + (peer_bda[0] << 24) + (peer_bda[1] << 16) + (peer_bda[2] << 8) + peer_bda[3], + (peer_bda[4] << 8) + peer_bda[5], uuid); + + if (GATT_GetConnIdIfConnected(gap_cb.gatt_if, peer_bda, &p_clcb->conn_id, BT_TRANSPORT_LE)) { + p_clcb->connected = TRUE; + } + + /* hold the link here */ + if (!GATT_Connect(gap_cb.gatt_if, p_clcb->bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, BT_TRANSPORT_LE, FALSE)) { + return started; + } + + /* enqueue the request */ + gap_ble_enqueue_request(p_clcb, uuid, p_cback); + + if (p_clcb->connected && p_clcb->cl_op_uuid == 0) { + started = gap_ble_send_cl_read_request(p_clcb); + } else { /* wait for connection up or pending operation to finish */ + started = TRUE; + } + + return started; +} +/******************************************************************************* +** +** Function GAP_BleReadPeerPrefConnParams +** +** Description Start a process to read a connected peripheral's preferred +** connection parameters +** +** Returns TRUE if read started, else FALSE if GAP is busy +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerPrefConnParams (BD_ADDR peer_bda) +{ + return gap_ble_accept_cl_operation (peer_bda, GATT_UUID_GAP_PREF_CONN_PARAM, NULL); +} + +/******************************************************************************* +** +** Function GAP_BleReadPeerDevName +** +** Description Start a process to read a connected peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerDevName (BD_ADDR peer_bda, tGAP_BLE_CMPL_CBACK *p_cback) +{ + return gap_ble_accept_cl_operation (peer_bda, GATT_UUID_GAP_DEVICE_NAME, p_cback); +} + +/******************************************************************************* +** +** Function GAP_BleReadPeerAddressResolutionCap +** +** Description Start a process to read peer address resolution capability +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleReadPeerAddressResolutionCap (BD_ADDR peer_bda, tGAP_BLE_CMPL_CBACK *p_cback) +{ + return gap_ble_accept_cl_operation(peer_bda, GATT_UUID_GAP_CENTRAL_ADDR_RESOL, p_cback); +} + +/******************************************************************************* +** +** Function GAP_BleCancelReadPeerDevName +** +** Description Cancel reading a peripheral's device name. +** +** Returns TRUE if request accepted +** +*******************************************************************************/ +BOOLEAN GAP_BleCancelReadPeerDevName (BD_ADDR peer_bda) +{ + tGAP_CLCB *p_clcb = gap_find_clcb_by_bd_addr (peer_bda); + + GAP_TRACE_EVENT ("GAP_BleCancelReadPeerDevName() - BDA: %08x%04x cl_op_uuid: 0x%04x", + (peer_bda[0] << 24) + (peer_bda[1] << 16) + (peer_bda[2] << 8) + peer_bda[3], + (peer_bda[4] << 8) + peer_bda[5], (p_clcb == NULL) ? 0 : p_clcb->cl_op_uuid); + + if (p_clcb == NULL) { + GAP_TRACE_ERROR ("Cannot cancel current op is not get dev name"); + return FALSE; + } + + if (!p_clcb->connected) { + if (!GATT_CancelConnect(gap_cb.gatt_if, peer_bda, TRUE)) { + GAP_TRACE_ERROR ("Cannot cancel where No connection id"); + return FALSE; + } + } + + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + + return (TRUE); +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE*/ diff --git a/lib/bt/host/bluedroid/stack/gap/gap_conn.c b/lib/bt/host/bluedroid/stack/gap/gap_conn.c new file mode 100644 index 00000000..db9065de --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_conn.c @@ -0,0 +1,1211 @@ +/****************************************************************************** + * + * 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 "common/bt_target.h" +#include "common/bt_defs.h" +#include "stack/btu.h" +#include "gap_int.h" +#include "stack/l2cdefs.h" +#include "l2c_int.h" +#include <string.h> +#include "osi/mutex.h" +#include "osi/allocator.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 ((defined AMP_INCLUDED) && (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 (const 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 ((defined AMP_INCLUDED) && (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_buf_size = GAP_DATA_BUF_SIZE; + p_ccb->ertm_info.user_tx_buf_size = GAP_DATA_BUF_SIZE; + p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE; + p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_INVALID_ERM_BUF_SIZE; + } 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); + UINT16 copy_len; + + if (!p_ccb) { + return (GAP_ERR_BAD_HANDLE); + } + + *p_len = 0; + + if (fixed_queue_is_empty(p_ccb->rx_queue)) { + return (GAP_NO_DATA_AVAIL); + } + + osi_mutex_global_lock(); + + while (max_len) { + BT_HDR *p_buf = fixed_queue_try_peek_first(p_ccb->rx_queue); + if (p_buf == NULL) { + break; + } + + 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; + } + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); + } + + p_ccb->rx_queue_size -= *p_len; + + osi_mutex_global_unlock(); + + 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 *)fixed_queue_dequeue(p_ccb->rx_queue, 0); + + 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) { + osi_free (p_buf); + return (GAP_ERR_BAD_HANDLE); + } + + if (p_ccb->con_state != GAP_CCB_STATE_CONNECTED) { + osi_free (p_buf); + return (GAP_ERR_BAD_STATE); + } + + if (p_buf->offset < L2CAP_MIN_OFFSET) { + osi_free (p_buf); + return (GAP_ERR_BUF_OFFSET); + } + + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + 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 *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != 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 *)osi_malloc(L2CAP_FCR_ERTM_BUF_SIZE)) == NULL) { + return (GAP_ERR_CONGESTED); + } + } else { + if ((p_buf = (BT_HDR *)osi_malloc(GAP_DATA_BUF_SIZE)) == 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); + + fixed_queue_enqueue(p_ccb->tx_queue, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + 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 *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != 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 = p_ccb->ertm_info.user_tx_buf_size + - 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) { + osi_free (p_msg); + return; + } + + if (p_ccb->con_state == GAP_CCB_STATE_CONNECTED) { + fixed_queue_enqueue(p_ccb->rx_queue, p_msg, FIXED_QUEUE_MAX_TIMEOUT); + + 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 { + osi_free (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 *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != 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->tx_queue = fixed_queue_new(QUEUE_SIZE_MAX); + p_ccb->rx_queue = fixed_queue_new(QUEUE_SIZE_MAX); + + 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 (!fixed_queue_is_empty(p_ccb->rx_queue)) { + osi_free(fixed_queue_dequeue(p_ccb->rx_queue, 0)); + } + fixed_queue_free(p_ccb->rx_queue, NULL); + p_ccb->rx_queue = NULL; + + while (!fixed_queue_is_empty(p_ccb->tx_queue)) { + osi_free(fixed_queue_dequeue(p_ccb->tx_queue, 0)); + } + fixed_queue_free(p_ccb->tx_queue, NULL); + p_ccb->tx_queue = NULL; + + 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; + } + } +#if (SDP_INCLUDED == TRUE) + /* Free the security record for this PSM */ + BTM_SecClrService(service_id); +#endif ///SDP_INCLUDED == TRUE + 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 *)osi_malloc(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."); + } +} + +#endif /* (GAP_CONN_POST_EVT_INCLUDED == TRUE) */ +#endif /* GAP_CONN_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gap/gap_utils.c b/lib/bt/host/bluedroid/stack/gap/gap_utils.c new file mode 100644 index 00000000..b358021a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/gap_utils.c @@ -0,0 +1,141 @@ +/****************************************************************************** + * + * 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 "common/bt_target.h" +//#include "bt_utils.h" +#include "gap_int.h" + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** 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); + } +} + +#endif ///CLASSIC_BT_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/gap/include/gap_int.h b/lib/bt/host/bluedroid/stack/gap/include/gap_int.h new file mode 100644 index 00000000..8a3ae0e2 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gap/include/gap_int.h @@ -0,0 +1,159 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + + +#ifndef GAP_INT_H +#define GAP_INT_H + +#include "common/bt_target.h" +#include "osi/fixed_queue.h" +#include "stack/gap_api.h" +#include "stack/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; + fixed_queue_t *tx_queue; /* Queue of buffers waiting to be sent */ + fixed_queue_t *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 ((defined AMP_INCLUDED) && (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 4 + +typedef struct { + UINT16 handle; + UINT16 uuid; + tGAP_BLE_ATTR_VALUE attr_value; +} tGAP_ATTR; +#endif +/********************************************************************** +** M A I N C O N T R O L B L O C K +***********************************************************************/ + +#define GAP_MAX_CL GATT_CL_MAX_LCB + +typedef struct { + UINT16 uuid; + tGAP_BLE_CMPL_CBACK *p_cback; +} tGAP_BLE_REQ; + +typedef struct { + BD_ADDR bda; + tGAP_BLE_CMPL_CBACK *p_cback; + UINT16 conn_id; + UINT16 cl_op_uuid; + BOOLEAN in_use; + BOOLEAN connected; + fixed_queue_t *pending_req_q; + +} tGAP_CLCB; + +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 && GATTS_INCLUDED == TRUE + tGAP_ATTR gap_attr[GAP_MAX_CHAR_NUM]; + tGAP_CLCB clcb[GAP_MAX_CL]; /* connection link*/ + tGATT_IF gatt_if; +#endif +} tGAP_CB; + +#if GAP_DYNAMIC_MEMORY == FALSE +extern tGAP_CB gap_cb; +#else +extern tGAP_CB *gap_cb_ptr; +#define gap_cb (*gap_cb_ptr) +#endif + +#if (GAP_CONN_INCLUDED == TRUE) +extern void gap_conn_init(void); +#endif +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) +extern void gap_attr_db_init(void); +#endif + +#endif |
