From 5738f83aeb59361a0a2eda2460113f6dc9194271 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 12 Dec 2012 16:00:35 -0800 Subject: Snapshot cdeccf6fdd8c2d494ea2867cb37a025bf8879baf Change-Id: Ia2de32ccb97a9641462c72363b0a8c4288f4f36d --- stack/Android.mk | 141 ++ stack/a2dp/a2d_api.c | 395 +++ stack/a2dp/a2d_int.h | 83 + stack/a2dp/a2d_sbc.c | 401 +++ stack/avct/avct_api.c | 465 ++++ stack/avct/avct_ccb.c | 150 ++ stack/avct/avct_defs.h | 62 + stack/avct/avct_int.h | 239 ++ stack/avct/avct_l2c.c | 434 ++++ stack/avct/avct_lcb.c | 471 ++++ stack/avct/avct_lcb_act.c | 702 ++++++ stack/avdt/avdt_ad.c | 628 +++++ stack/avdt/avdt_api.c | 1383 +++++++++++ stack/avdt/avdt_ccb.c | 464 ++++ stack/avdt/avdt_ccb_act.c | 1108 +++++++++ stack/avdt/avdt_defs.h | 203 ++ stack/avdt/avdt_int.h | 744 ++++++ stack/avdt/avdt_l2c.c | 521 ++++ stack/avdt/avdt_msg.c | 1891 ++++++++++++++ stack/avdt/avdt_scb.c | 800 ++++++ stack/avdt/avdt_scb_act.c | 2124 ++++++++++++++++ stack/avrc/avrc_api.c | 588 +++++ stack/avrc/avrc_int.h | 138 ++ stack/avrc/avrc_opt.c | 230 ++ stack/avrc/avrc_sdp.c | 297 +++ stack/bnep/bnep_api.c | 781 ++++++ stack/bnep/bnep_int.h | 251 ++ stack/bnep/bnep_main.c | 838 +++++++ stack/bnep/bnep_utils.c | 1463 +++++++++++ stack/btm/btm_acl.c | 3141 +++++++++++++++++++++++ stack/btm/btm_ble.c | 1900 ++++++++++++++ stack/btm/btm_ble_addr.c | 383 +++ stack/btm/btm_ble_bgconn.c | 616 +++++ stack/btm/btm_ble_gap.c | 2084 ++++++++++++++++ stack/btm/btm_ble_int.h | 299 +++ stack/btm/btm_dev.c | 464 ++++ stack/btm/btm_devctl.c | 1953 +++++++++++++++ stack/btm/btm_inq.c | 3265 ++++++++++++++++++++++++ stack/btm/btm_int.h | 1114 +++++++++ stack/btm/btm_main.c | 70 + stack/btm/btm_pm.c | 998 ++++++++ stack/btm/btm_sco.c | 1750 +++++++++++++ stack/btm/btm_sec.c | 5649 ++++++++++++++++++++++++++++++++++++++++++ stack/btu/btu_hcif.c | 2257 +++++++++++++++++ stack/btu/btu_init.c | 157 ++ stack/btu/btu_task.c | 835 +++++++ stack/gatt/att_protocol.c | 630 +++++ stack/gatt/gatt_api.c | 1555 ++++++++++++ stack/gatt/gatt_attr.c | 278 +++ stack/gatt/gatt_auth.c | 448 ++++ stack/gatt/gatt_cl.c | 1220 +++++++++ stack/gatt/gatt_db.c | 1130 +++++++++ stack/gatt/gatt_int.h | 671 +++++ stack/gatt/gatt_main.c | 1115 +++++++++ stack/gatt/gatt_sr.c | 1486 +++++++++++ stack/gatt/gatt_utils.c | 2600 +++++++++++++++++++ stack/hcic/hciblecmds.c | 751 ++++++ stack/hcic/hcicmds.c | 3361 +++++++++++++++++++++++++ stack/hid/hid_conn.h | 69 + stack/hid/hidh_api.c | 546 ++++ stack/hid/hidh_conn.c | 1040 ++++++++ stack/hid/hidh_int.h | 94 + stack/include/a2d_api.h | 257 ++ stack/include/a2d_sbc.h | 212 ++ stack/include/avct_api.h | 279 +++ stack/include/avdt_api.h | 902 +++++++ stack/include/avdtc_api.h | 231 ++ stack/include/avrc_api.h | 634 +++++ stack/include/avrc_defs.h | 1419 +++++++++++ stack/include/bnep_api.h | 476 ++++ stack/include/bt_types.h | 690 ++++++ stack/include/btm_api.h | 4556 ++++++++++++++++++++++++++++++++++ stack/include/btm_ble_api.h | 729 ++++++ stack/include/btu.h | 310 +++ stack/include/dyn_mem.h | 187 ++ stack/include/gatt_api.h | 1128 +++++++++ stack/include/gattdefs.h | 109 + stack/include/goep_fs.h | 393 +++ stack/include/hcidefs.h | 2233 +++++++++++++++++ stack/include/hcimsgs.h | 1331 ++++++++++ stack/include/hiddefs.h | 158 ++ stack/include/hidh_api.h | 222 ++ stack/include/l2c_api.h | 1185 +++++++++ stack/include/l2cdefs.h | 309 +++ stack/include/mca_api.h | 495 ++++ stack/include/mca_defs.h | 87 + stack/include/obx_api.h | 1704 +++++++++++++ stack/include/pan_api.h | 459 ++++ stack/include/port_api.h | 635 +++++ stack/include/port_ext.h | 32 + stack/include/rfcdefs.h | 248 ++ stack/include/sdp_api.h | 756 ++++++ stack/include/sdpdefs.h | 319 +++ stack/include/smp_api.h | 300 +++ stack/include/uipc_msg.h | 884 +++++++ stack/include/utfc.h | 61 + stack/include/wbt_api.h | 71 + stack/include/wcassert.h | 65 + stack/l2cap/l2c_api.c | 1812 ++++++++++++++ stack/l2cap/l2c_ble.c | 599 +++++ stack/l2cap/l2c_csm.c | 1333 ++++++++++ stack/l2cap/l2c_fcr.c | 2709 ++++++++++++++++++++ stack/l2cap/l2c_int.h | 771 ++++++ stack/l2cap/l2c_link.c | 1719 +++++++++++++ stack/l2cap/l2c_main.c | 991 ++++++++ stack/l2cap/l2c_ucd.c | 1170 +++++++++ stack/l2cap/l2c_utils.c | 3406 +++++++++++++++++++++++++ stack/mcap/mca_api.c | 925 +++++++ stack/mcap/mca_cact.c | 580 +++++ stack/mcap/mca_csm.c | 385 +++ stack/mcap/mca_dact.c | 162 ++ stack/mcap/mca_dsm.c | 346 +++ stack/mcap/mca_int.h | 356 +++ stack/mcap/mca_l2c.c | 589 +++++ stack/mcap/mca_main.c | 643 +++++ stack/pan/pan_api.c | 853 +++++++ stack/pan/pan_int.h | 158 ++ stack/pan/pan_main.c | 730 ++++++ stack/pan/pan_utils.c | 351 +++ stack/rfcomm/port_api.c | 1730 +++++++++++++ stack/rfcomm/port_int.h | 252 ++ stack/rfcomm/port_rfc.c | 1112 +++++++++ stack/rfcomm/port_utils.c | 582 +++++ stack/rfcomm/rfc_int.h | 387 +++ stack/rfcomm/rfc_l2cap_if.c | 443 ++++ stack/rfcomm/rfc_mx_fsm.c | 663 +++++ stack/rfcomm/rfc_port_fsm.c | 915 +++++++ stack/rfcomm/rfc_port_if.c | 339 +++ stack/rfcomm/rfc_ts_frames.c | 908 +++++++ stack/rfcomm/rfc_utils.c | 467 ++++ stack/sdp/sdp_api.c | 1475 +++++++++++ stack/sdp/sdp_db.c | 962 +++++++ stack/sdp/sdp_discovery.c | 1127 +++++++++ stack/sdp/sdp_main.c | 724 ++++++ stack/sdp/sdp_server.c | 835 +++++++ stack/sdp/sdp_utils.c | 1040 ++++++++ stack/sdp/sdpint.h | 329 +++ stack/smp/aes.c | 926 +++++++ stack/smp/aes.h | 162 ++ stack/smp/smp_act.c | 950 +++++++ stack/smp/smp_api.c | 337 +++ stack/smp/smp_cmac.c | 388 +++ stack/smp/smp_int.h | 315 +++ stack/smp/smp_keys.c | 909 +++++++ stack/smp/smp_l2c.c | 161 ++ stack/smp/smp_main.c | 518 ++++ stack/smp/smp_utils.c | 675 +++++ 147 files changed, 126209 insertions(+) create mode 100644 stack/Android.mk create mode 100644 stack/a2dp/a2d_api.c create mode 100644 stack/a2dp/a2d_int.h create mode 100644 stack/a2dp/a2d_sbc.c create mode 100644 stack/avct/avct_api.c create mode 100644 stack/avct/avct_ccb.c create mode 100644 stack/avct/avct_defs.h create mode 100644 stack/avct/avct_int.h create mode 100644 stack/avct/avct_l2c.c create mode 100644 stack/avct/avct_lcb.c create mode 100644 stack/avct/avct_lcb_act.c create mode 100644 stack/avdt/avdt_ad.c create mode 100644 stack/avdt/avdt_api.c create mode 100644 stack/avdt/avdt_ccb.c create mode 100644 stack/avdt/avdt_ccb_act.c create mode 100644 stack/avdt/avdt_defs.h create mode 100644 stack/avdt/avdt_int.h create mode 100644 stack/avdt/avdt_l2c.c create mode 100644 stack/avdt/avdt_msg.c create mode 100644 stack/avdt/avdt_scb.c create mode 100644 stack/avdt/avdt_scb_act.c create mode 100644 stack/avrc/avrc_api.c create mode 100644 stack/avrc/avrc_int.h create mode 100644 stack/avrc/avrc_opt.c create mode 100644 stack/avrc/avrc_sdp.c create mode 100644 stack/bnep/bnep_api.c create mode 100644 stack/bnep/bnep_int.h create mode 100644 stack/bnep/bnep_main.c create mode 100644 stack/bnep/bnep_utils.c create mode 100644 stack/btm/btm_acl.c create mode 100644 stack/btm/btm_ble.c create mode 100644 stack/btm/btm_ble_addr.c create mode 100644 stack/btm/btm_ble_bgconn.c create mode 100644 stack/btm/btm_ble_gap.c create mode 100644 stack/btm/btm_ble_int.h create mode 100644 stack/btm/btm_dev.c create mode 100644 stack/btm/btm_devctl.c create mode 100644 stack/btm/btm_inq.c create mode 100644 stack/btm/btm_int.h create mode 100644 stack/btm/btm_main.c create mode 100644 stack/btm/btm_pm.c create mode 100644 stack/btm/btm_sco.c create mode 100644 stack/btm/btm_sec.c create mode 100644 stack/btu/btu_hcif.c create mode 100644 stack/btu/btu_init.c create mode 100644 stack/btu/btu_task.c create mode 100644 stack/gatt/att_protocol.c create mode 100644 stack/gatt/gatt_api.c create mode 100644 stack/gatt/gatt_attr.c create mode 100644 stack/gatt/gatt_auth.c create mode 100644 stack/gatt/gatt_cl.c create mode 100644 stack/gatt/gatt_db.c create mode 100644 stack/gatt/gatt_int.h create mode 100644 stack/gatt/gatt_main.c create mode 100644 stack/gatt/gatt_sr.c create mode 100644 stack/gatt/gatt_utils.c create mode 100644 stack/hcic/hciblecmds.c create mode 100644 stack/hcic/hcicmds.c create mode 100644 stack/hid/hid_conn.h create mode 100644 stack/hid/hidh_api.c create mode 100644 stack/hid/hidh_conn.c create mode 100644 stack/hid/hidh_int.h create mode 100644 stack/include/a2d_api.h create mode 100644 stack/include/a2d_sbc.h create mode 100644 stack/include/avct_api.h create mode 100644 stack/include/avdt_api.h create mode 100644 stack/include/avdtc_api.h create mode 100644 stack/include/avrc_api.h create mode 100644 stack/include/avrc_defs.h create mode 100644 stack/include/bnep_api.h create mode 100644 stack/include/bt_types.h create mode 100644 stack/include/btm_api.h create mode 100644 stack/include/btm_ble_api.h create mode 100644 stack/include/btu.h create mode 100644 stack/include/dyn_mem.h create mode 100644 stack/include/gatt_api.h create mode 100644 stack/include/gattdefs.h create mode 100644 stack/include/goep_fs.h create mode 100644 stack/include/hcidefs.h create mode 100644 stack/include/hcimsgs.h create mode 100644 stack/include/hiddefs.h create mode 100644 stack/include/hidh_api.h create mode 100644 stack/include/l2c_api.h create mode 100644 stack/include/l2cdefs.h create mode 100644 stack/include/mca_api.h create mode 100644 stack/include/mca_defs.h create mode 100644 stack/include/obx_api.h create mode 100644 stack/include/pan_api.h create mode 100644 stack/include/port_api.h create mode 100644 stack/include/port_ext.h create mode 100644 stack/include/rfcdefs.h create mode 100644 stack/include/sdp_api.h create mode 100644 stack/include/sdpdefs.h create mode 100644 stack/include/smp_api.h create mode 100644 stack/include/uipc_msg.h create mode 100644 stack/include/utfc.h create mode 100644 stack/include/wbt_api.h create mode 100644 stack/include/wcassert.h create mode 100644 stack/l2cap/l2c_api.c create mode 100644 stack/l2cap/l2c_ble.c create mode 100644 stack/l2cap/l2c_csm.c create mode 100644 stack/l2cap/l2c_fcr.c create mode 100644 stack/l2cap/l2c_int.h create mode 100644 stack/l2cap/l2c_link.c create mode 100644 stack/l2cap/l2c_main.c create mode 100644 stack/l2cap/l2c_ucd.c create mode 100644 stack/l2cap/l2c_utils.c create mode 100644 stack/mcap/mca_api.c create mode 100644 stack/mcap/mca_cact.c create mode 100644 stack/mcap/mca_csm.c create mode 100644 stack/mcap/mca_dact.c create mode 100644 stack/mcap/mca_dsm.c create mode 100644 stack/mcap/mca_int.h create mode 100644 stack/mcap/mca_l2c.c create mode 100644 stack/mcap/mca_main.c create mode 100644 stack/pan/pan_api.c create mode 100644 stack/pan/pan_int.h create mode 100644 stack/pan/pan_main.c create mode 100644 stack/pan/pan_utils.c create mode 100644 stack/rfcomm/port_api.c create mode 100644 stack/rfcomm/port_int.h create mode 100644 stack/rfcomm/port_rfc.c create mode 100644 stack/rfcomm/port_utils.c create mode 100644 stack/rfcomm/rfc_int.h create mode 100644 stack/rfcomm/rfc_l2cap_if.c create mode 100644 stack/rfcomm/rfc_mx_fsm.c create mode 100644 stack/rfcomm/rfc_port_fsm.c create mode 100644 stack/rfcomm/rfc_port_if.c create mode 100644 stack/rfcomm/rfc_ts_frames.c create mode 100644 stack/rfcomm/rfc_utils.c create mode 100644 stack/sdp/sdp_api.c create mode 100644 stack/sdp/sdp_db.c create mode 100644 stack/sdp/sdp_discovery.c create mode 100644 stack/sdp/sdp_main.c create mode 100644 stack/sdp/sdp_server.c create mode 100644 stack/sdp/sdp_utils.c create mode 100644 stack/sdp/sdpint.h create mode 100644 stack/smp/aes.c create mode 100644 stack/smp/aes.h create mode 100644 stack/smp/smp_act.c create mode 100644 stack/smp/smp_api.c create mode 100644 stack/smp/smp_cmac.c create mode 100644 stack/smp/smp_int.h create mode 100644 stack/smp/smp_keys.c create mode 100644 stack/smp/smp_l2c.c create mode 100644 stack/smp/smp_main.c create mode 100644 stack/smp/smp_utils.c (limited to 'stack') diff --git a/stack/Android.mk b/stack/Android.mk new file mode 100644 index 0000000..0c24805 --- /dev/null +++ b/stack/Android.mk @@ -0,0 +1,141 @@ +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_C_INCLUDES:= . \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/avct \ + $(LOCAL_PATH)/btm \ + $(LOCAL_PATH)/avrc \ + $(LOCAL_PATH)/l2cap \ + $(LOCAL_PATH)/avdt \ + $(LOCAL_PATH)/gatt \ + $(LOCAL_PATH)/gap \ + $(LOCAL_PATH)/pan \ + $(LOCAL_PATH)/bnep \ + $(LOCAL_PATH)/hid \ + $(LOCAL_PATH)/sdp \ + $(LOCAL_PATH)/smp \ + $(LOCAL_PATH)/../include \ + $(LOCAL_PATH)/../gki/common \ + $(LOCAL_PATH)/../gki/ulinux \ + $(LOCAL_PATH)/../udrv/include \ + $(LOCAL_PATH)/../rpc/include \ + $(LOCAL_PATH)/../hcis \ + $(LOCAL_PATH)/../ctrlr/include \ + $(LOCAL_PATH)/../bta/include \ + $(LOCAL_PATH)/../bta/sys \ + $(LOCAL_PATH)/../brcm/include \ + $(LOCAL_PATH)/../utils/include \ + $(bdroid_C_INCLUDES) \ + +LOCAL_CFLAGS += $(bdroid_CFLAGS) + +ifeq ($(BOARD_HAVE_BLUETOOTH_BCM),true) +LOCAL_CFLAGS += \ + -DBOARD_HAVE_BLUETOOTH_BCM +endif + +LOCAL_PRELINK_MODULE:=false +LOCAL_SRC_FILES:= \ + ./a2dp/a2d_api.c \ + ./a2dp/a2d_sbc.c \ + ./avrc/avrc_api.c \ + ./avrc/avrc_sdp.c \ + ./avrc/avrc_opt.c \ + ./hid/hidh_api.c \ + ./hid/hidh_conn.c \ + ./bnep/bnep_main.c \ + ./bnep/bnep_utils.c \ + ./bnep/bnep_api.c \ + ./hcic/hciblecmds.c \ + ./hcic/hcicmds.c \ + ./btm/btm_ble.c \ + ./btm/btm_sec.c \ + ./btm/btm_inq.c \ + ./btm/btm_ble_addr.c \ + ./btm/btm_ble_bgconn.c \ + ./btm/btm_main.c \ + ./btm/btm_dev.c \ + ./btm/btm_ble_gap.c \ + ./btm/btm_acl.c \ + ./btm/btm_sco.c \ + ./btm/btm_pm.c \ + ./btm/btm_devctl.c \ + ./rfcomm/rfc_utils.c \ + ./rfcomm/port_rfc.c \ + ./rfcomm/rfc_l2cap_if.c \ + ./rfcomm/rfc_mx_fsm.c \ + ./rfcomm/port_utils.c \ + ./rfcomm/rfc_port_fsm.c \ + ./rfcomm/rfc_port_if.c \ + ./rfcomm/port_api.c \ + ./rfcomm/rfc_ts_frames.c \ + ./mcap/mca_dact.c \ + ./mcap/mca_dsm.c \ + ./mcap/mca_l2c.c \ + ./mcap/mca_main.c \ + ./mcap/mca_csm.c \ + ./mcap/mca_cact.c \ + ./mcap/mca_api.c \ + ./gatt/gatt_sr.c \ + ./gatt/gatt_cl.c \ + ./gatt/gatt_api.c \ + ./gatt/gatt_auth.c \ + ./gatt/gatt_utils.c \ + ./gatt/gatt_main.c \ + ./gatt/att_protocol.c \ + ./gatt/gatt_attr.c \ + ./gatt/gatt_db.c \ + ./avct/avct_api.c \ + ./avct/avct_l2c.c \ + ./avct/avct_lcb.c \ + ./avct/avct_ccb.c \ + ./avct/avct_lcb_act.c \ + ./smp/smp_main.c \ + ./smp/smp_l2c.c \ + ./smp/smp_cmac.c \ + ./smp/smp_utils.c \ + ./smp/smp_act.c \ + ./smp/smp_keys.c \ + ./smp/smp_api.c \ + ./smp/aes.c \ + ./avdt/avdt_ccb.c \ + ./avdt/avdt_scb_act.c \ + ./avdt/avdt_msg.c \ + ./avdt/avdt_ccb_act.c \ + ./avdt/avdt_api.c \ + ./avdt/avdt_scb.c \ + ./avdt/avdt_ad.c \ + ./avdt/avdt_l2c.c \ + ./sdp/sdp_server.c \ + ./sdp/sdp_main.c \ + ./sdp/sdp_db.c \ + ./sdp/sdp_utils.c \ + ./sdp/sdp_api.c \ + ./sdp/sdp_discovery.c \ + ./pan/pan_main.c \ + ./pan/pan_api.c \ + ./pan/pan_utils.c \ + ./btu/btu_hcif.c \ + ./btu/btu_init.c \ + ./btu/btu_task.c \ + ./l2cap/l2c_fcr.c \ + ./l2cap/l2c_ucd.c \ + ./l2cap/l2c_main.c \ + ./l2cap/l2c_api.c \ + ./l2cap/l2c_utils.c \ + ./l2cap/l2c_csm.c \ + ./l2cap/l2c_link.c \ + ./l2cap/l2c_ble.c + +LOCAL_MODULE := libbt-brcm_stack +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +LOCAL_SHARED_LIBRARIES := libcutils libc + +include $(BUILD_STATIC_LIBRARY) + +endif # TARGET_SIMULATOR != true diff --git a/stack/a2dp/a2d_api.c b/stack/a2dp/a2d_api.c new file mode 100644 index 0000000..71dcc13 --- /dev/null +++ b/stack/a2dp/a2d_api.c @@ -0,0 +1,395 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * ommon API for the Advanced Audio Distribution Profile (A2DP) + * + ******************************************************************************/ +#include +#include "bt_target.h" +#include "sdpdefs.h" +#include "a2d_api.h" +#include "a2d_int.h" +#include "avdt_api.h" + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if A2D_DYNAMIC_MEMORY == FALSE +tA2D_CB a2d_cb; +#endif + + +/****************************************************************************** +** +** Function a2d_sdp_cback +** +** Description This is the SDP callback function used by A2D_FindService. +** This function will be executed by SDP when the service +** search is completed. If the search is successful, it +** finds the first record in the database that matches the +** UUID of the search. Then retrieves various parameters +** from the record. When it is finished it calls the +** application callback function. +** +** Returns Nothing. +** +******************************************************************************/ +static void a2d_sdp_cback(UINT16 status) +{ + tSDP_DISC_REC *p_rec = NULL; + tSDP_DISC_ATTR *p_attr; + BOOLEAN found = FALSE; + tA2D_Service a2d_svc; + tSDP_PROTOCOL_ELEM elem; + + A2D_TRACE_API1("a2d_sdp_cback status: %d", status); + + if (status == SDP_SUCCESS) + { + /* loop through all records we found */ + do + { + /* get next record; if none found, we're done */ + if ((p_rec = SDP_FindServiceInDb(a2d_cb.find.p_db, + a2d_cb.find.service_uuid, p_rec)) == NULL) + { + break; + } + memset(&a2d_svc, 0, sizeof(tA2D_Service)); + + /* get service name */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_SERVICE_NAME)) != NULL) + { + a2d_svc.p_service_name = (char *) p_attr->attr_value.v.array; + a2d_svc.service_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + } + + /* get provider name */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_PROVIDER_NAME)) != NULL) + { + a2d_svc.p_provider_name = (char *) p_attr->attr_value.v.array; + a2d_svc.provider_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type); + } + + /* get supported features */ + if ((p_attr = SDP_FindAttributeInRec(p_rec, + ATTR_ID_SUPPORTED_FEATURES)) != NULL) + { + a2d_svc.features = p_attr->attr_value.v.u16; + } + + /* get AVDTP version */ + if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_AVDTP, &elem)) + { + a2d_svc.avdt_version = elem.params[0]; + A2D_TRACE_DEBUG1("avdt_version: 0x%x", a2d_svc.avdt_version); + } + + /* we've got everything, we're done */ + found = TRUE; + break; + + } while (TRUE); + } + + a2d_cb.find.service_uuid = 0; + /* return info from sdp record in app callback function */ + if (a2d_cb.find.p_cback != NULL) + { + (*a2d_cb.find.p_cback)(found, &a2d_svc); + } + + return; +} + +/******************************************************************************* +** +** Function a2d_set_avdt_sdp_ver +** +** Description This function allows the script wrapper to change the +** avdt version of a2dp. +** +** Returns None +** +*******************************************************************************/ +void a2d_set_avdt_sdp_ver (UINT16 avdt_sdp_ver) +{ + a2d_cb.avdt_sdp_ver = avdt_sdp_ver; +} + +/****************************************************************************** +** +** Function A2D_AddRecord +** +** Description This function is called by a server application to add +** SRC or SNK information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** +** features: Profile supported features. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +tA2D_STATUS A2D_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 features, UINT32 sdp_handle) +{ + UINT16 browse_list[1]; + BOOLEAN result = TRUE; + UINT8 temp[8]; + UINT8 *p; + tSDP_PROTOCOL_ELEM proto_list [A2D_NUM_PROTO_ELEMS]; + + A2D_TRACE_API1("A2D_AddRecord uuid: %x", service_uuid); + + if( (sdp_handle == 0) || + (service_uuid != UUID_SERVCLASS_AUDIO_SOURCE && service_uuid != UUID_SERVCLASS_AUDIO_SINK) ) + return A2D_INVALID_PARAMS; + + /* add service class id list */ + result &= SDP_AddServiceClassIdList(sdp_handle, 1, &service_uuid); + + memset((void*) proto_list, 0 , A2D_NUM_PROTO_ELEMS*sizeof(tSDP_PROTOCOL_ELEM)); + + /* add protocol descriptor list */ + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 1; + proto_list[0].params[0] = AVDT_PSM; + proto_list[1].protocol_uuid = UUID_PROTOCOL_AVDTP; + proto_list[1].num_params = 1; + proto_list[1].params[0] = a2d_cb.avdt_sdp_ver; + + result &= SDP_AddProtocolList(sdp_handle, A2D_NUM_PROTO_ELEMS, proto_list); + + /* add profile descriptor list */ + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, A2D_VERSION); + + /* add supported feature */ + if (features != 0) + { + p = temp; + UINT16_TO_BE_STREAM(p, features); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8*)temp); + } + + /* add provider name */ + if (p_provider_name != NULL) + { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_provider_name)+1), (UINT8 *) p_provider_name); + } + + /* add service name */ + if (p_service_name != NULL) + { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name); + } + + /* add browse group list */ + browse_list[0] = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + + + return (result ? A2D_SUCCESS : A2D_FAIL); +} + +/****************************************************************************** +** +** Function A2D_FindService +** +** Description This function is called by a client application to +** perform service discovery and retrieve SRC or SNK SDP +** record information from a server. Information is +** returned for the first service record found on the +** server that matches the service UUID. The callback +** function will be executed when service discovery is +** complete. There can only be one outstanding call to +** A2D_FindService() at a time; the application must wait +** for the callback before it makes another call to +** the function. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** bd_addr: BD address of the peer device. +** +** p_db: Pointer to the information to initialize +** the discovery database. +** +** p_cback: Pointer to the A2D_FindService() +** callback function. +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_BUSY if discovery is already in progress. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +tA2D_STATUS A2D_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tA2D_SDP_DB_PARAMS *p_db, tA2D_FIND_CBACK *p_cback) +{ + tSDP_UUID uuid_list; + BOOLEAN result = TRUE; + UINT16 a2d_attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, /* update A2D_NUM_ATTR, if changed */ + ATTR_ID_BT_PROFILE_DESC_LIST, + ATTR_ID_SUPPORTED_FEATURES, + ATTR_ID_SERVICE_NAME, + ATTR_ID_PROTOCOL_DESC_LIST, + ATTR_ID_PROVIDER_NAME}; + + A2D_TRACE_API1("A2D_FindService uuid: %x", service_uuid); + if( (service_uuid != UUID_SERVCLASS_AUDIO_SOURCE && service_uuid != UUID_SERVCLASS_AUDIO_SINK) || + p_db == NULL || p_db->p_db == NULL || p_cback == NULL) + return A2D_INVALID_PARAMS; + + if( a2d_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SOURCE || + a2d_cb.find.service_uuid == UUID_SERVCLASS_AUDIO_SINK) + return A2D_BUSY; + + /* set up discovery database */ + uuid_list.len = LEN_UUID_16; + uuid_list.uu.uuid16 = service_uuid; + + if(p_db->p_attrs == NULL || p_db->num_attr == 0) + { + p_db->p_attrs = a2d_attr_list; + p_db->num_attr = A2D_NUM_ATTR; + } + + result = SDP_InitDiscoveryDb(p_db->p_db, p_db->db_len, 1, &uuid_list, p_db->num_attr, + p_db->p_attrs); + + if (result == TRUE) + { + /* store service_uuid and discovery db pointer */ + a2d_cb.find.p_db = p_db->p_db; + a2d_cb.find.service_uuid = service_uuid; + a2d_cb.find.p_cback = p_cback; + + /* perform service search */ + result = SDP_ServiceSearchAttributeRequest(bd_addr, p_db->p_db, a2d_sdp_cback); + if(FALSE == result) + { + a2d_cb.find.service_uuid = 0; + } + } + + return (result ? A2D_SUCCESS : A2D_FAIL); +} + +/****************************************************************************** +** +** Function A2D_SetTraceLevel +** +** Description Sets the trace level for A2D. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the A2D tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 A2D_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + a2d_cb.trace_level = new_level; + + return (a2d_cb.trace_level); +} + +/****************************************************************************** +** Function A2D_BitsSet +** +** Description Check the given num for the number of bits set +** Returns A2D_SET_ONE_BIT, if one and only one bit is set +** A2D_SET_ZERO_BIT, if all bits clear +** A2D_SET_MULTL_BIT, if multiple bits are set +******************************************************************************/ +UINT8 A2D_BitsSet(UINT8 num) +{ + UINT8 count; + BOOLEAN res; + if(num == 0) + res = A2D_SET_ZERO_BIT; + else + { + count = (num & (num - 1)); + res = ((count==0)?A2D_SET_ONE_BIT:A2D_SET_MULTL_BIT); + } + return res; +} + +/******************************************************************************* +** +** Function A2D_Init +** +** Description This function is called to initialize the control block +** for this layer. It must be called before accessing any +** other API functions for this layer. It is typically called +** once during the start up of the stack. +** +** Returns void +** +*******************************************************************************/ +void A2D_Init(void) +{ + memset(&a2d_cb, 0, sizeof(tA2D_CB)); + + a2d_cb.avdt_sdp_ver = AVDT_VERSION; + +#if defined(A2D_INITIAL_TRACE_LEVEL) + a2d_cb.trace_level = A2D_INITIAL_TRACE_LEVEL; +#else + a2d_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + diff --git a/stack/a2dp/a2d_int.h b/stack/a2dp/a2d_int.h new file mode 100644 index 0000000..4aaa083 --- /dev/null +++ b/stack/a2dp/a2d_int.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * 2DP internal header file + * + ******************************************************************************/ +#ifndef A2D_INT_H +#define A2D_INT_H + +#include "a2d_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#define A2D_VERSION 0x0102 + +/* Number of attributes in A2D SDP record. */ +#define A2D_NUM_ATTR 6 + +/* Number of protocol elements in protocol element list. */ +#define A2D_NUM_PROTO_ELEMS 2 + +/***************************************************************************** +** Type definitions +*****************************************************************************/ + +/* Control block used by A2D_FindService(). */ +typedef struct +{ + tA2D_FIND_CBACK *p_cback; /* pointer to application callback */ + tSDP_DISCOVERY_DB *p_db; /* pointer to discovery database */ + UINT16 service_uuid; /* service UUID of search */ +} tA2D_FIND_CB; + +typedef struct +{ + tA2D_FIND_CB find; /* find service control block */ + UINT8 trace_level; + BOOLEAN use_desc; + UINT16 avdt_sdp_ver; /* AVDTP version */ +} tA2D_CB; + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if A2D_DYNAMIC_MEMORY == FALSE +A2D_API extern tA2D_CB a2d_cb; +#else +A2D_API extern tA2D_CB *a2d_cb_ptr; +#define a2d_cb (*a2d_cb_ptr) +#endif + +/* Used only for conformance testing */ +A2D_API extern void a2d_set_avdt_sdp_ver (UINT16 avdt_sdp_ver); + +#ifdef __cplusplus +} +#endif + +#endif /* A2D_INT_H */ diff --git a/stack/a2dp/a2d_sbc.c b/stack/a2dp/a2d_sbc.c new file mode 100644 index 0000000..a4e5255 --- /dev/null +++ b/stack/a2dp/a2d_sbc.c @@ -0,0 +1,401 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Utility functions to help build and parse SBC Codec Information Element + * and Media Payload. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if (A2D_SBC_INCLUDED == TRUE) +#include +#include "a2d_api.h" +#include "a2d_int.h" +#include "a2d_sbc.h" + +/************************************************************************************************* + * SBC descramble code + * Purpose: to tie the SBC code with BTE/mobile stack code, + * especially for the case when the SBC is ported into a third-party Multimedia chip + * + * Algorithm: + * init process: all counters reset to 0, + * calculate base_index: (6 + s16NumOfChannels*s16NumOfSubBands/2) + * scramble side: the init process happens every time SBC_Encoder_Init() is called. + * descramble side: it would be nice to know if he "init" process has happened. + * alter the SBC SYNC word 0x9C (1001 1100) to 0x8C (1000 1100). + * + * scramble process: + * The CRC byte: + * Every SBC frame has a frame header. + * The 1st byte is the sync word and the following 2 bytes are about the stream format. + * They are supposed to be "constant" within a "song" + * The 4th byte is the CRC byte. The CRC byte is bound to be random. + * Derive 2 items from the CRC byte; one is the "use" bit, the other is the "index". + * + * SBC keeps 2 sets of "use" & "index"; derived the current and the previous frame. + * + * The "use" bit is any bit in SBC_PRTC_USE_MASK is set. + * If set, SBC uses the "index" from the current frame. + * If not set, SBC uses the "index" from the previous frame or 0. + * + * index = (CRC & 0x3) + ((CRC & 0x30) >> 2) // 8 is the max index + * + * if(index > 0) + * { + * p = &u8frame[base_index]; + * if((index&1)&&(u16PacketLength > (base_index+index*2))) + * { + * // odd index: swap 2 bytes + * tmp = p[index]; + * p[index] = p[index*2]; + * p[index*2] = tmp; + * } + * else + * { + * // even index: shift by 3 + * tmp = (p[index] >> 3) + (p[index] << 5); + * p[index] = tmp; + * } + * } + * //else index is 0. The frame stays unaltered + * + */ +#define A2D_SBC_SYNC_WORD 0x9C +#define A2D_SBC_CRC_IDX 3 +#define A2D_SBC_USE_MASK 0x64 +#define A2D_SBC_SYNC_MASK 0x10 +#define A2D_SBC_CIDX 0 +#define A2D_SBC_LIDX 1 +#define A2D_SBC_CH_M_BITS 0xC /* channel mode bits: 0: mono; 1 ch */ +#define A2D_SBC_SUBBAND_BIT 0x1 /* num of subband bit: 0:4; 1: 8 */ + +#define A2D_SBC_GET_IDX(sc) (((sc) & 0x3) + (((sc) & 0x30) >> 2)) + +typedef struct +{ + UINT8 use; + UINT8 idx; +} tA2D_SBC_FR_CB; + +typedef struct +{ + tA2D_SBC_FR_CB fr[2]; + UINT8 index; + UINT8 base; +} tA2D_SBC_DS_CB; + +static tA2D_SBC_DS_CB a2d_sbc_ds_cb; +/*int a2d_count = 0;*/ +/****************************************************************************** +** +** Function A2D_SbcChkFrInit +** +** Description check if need to init the descramble control block. +** +** Returns nothing. +******************************************************************************/ +void A2D_SbcChkFrInit(UINT8 *p_pkt) +{ + UINT8 fmt; + UINT8 num_chnl = 1; + UINT8 num_subband = 4; + + if((p_pkt[0] & A2D_SBC_SYNC_MASK) == 0) + { + a2d_cb.use_desc = TRUE; + fmt = p_pkt[1]; + p_pkt[0] |= A2D_SBC_SYNC_MASK; + memset(&a2d_sbc_ds_cb, 0, sizeof(tA2D_SBC_DS_CB)); + if(fmt & A2D_SBC_CH_M_BITS) + num_chnl = 2; + if(fmt & A2D_SBC_SUBBAND_BIT) + num_subband = 8; + a2d_sbc_ds_cb.base = 6 + num_chnl*num_subband/2; + /*printf("base: %d\n", a2d_sbc_ds_cb.base); + a2d_count = 0;*/ + } +} + +/****************************************************************************** +** +** Function A2D_SbcDescramble +** +** Description descramble the packet. +** +** Returns nothing. +******************************************************************************/ +void A2D_SbcDescramble(UINT8 *p_pkt, UINT16 len) +{ + tA2D_SBC_FR_CB *p_cur, *p_last; + UINT32 idx, tmp, tmp2; + + if(a2d_cb.use_desc) + { + /* c2l */ + p_last = &a2d_sbc_ds_cb.fr[A2D_SBC_LIDX]; + p_cur = &a2d_sbc_ds_cb.fr[A2D_SBC_CIDX]; + p_last->idx = p_cur->idx; + p_last->use = p_cur->use; + /* getc */ + p_cur->use = p_pkt[A2D_SBC_CRC_IDX] & A2D_SBC_USE_MASK; + p_cur->idx = A2D_SBC_GET_IDX(p_pkt[A2D_SBC_CRC_IDX]); + a2d_sbc_ds_cb.index = (p_cur->use)?A2D_SBC_CIDX:A2D_SBC_LIDX; + /* + printf("%05d: ar[%02d]: x%02x, msk: x%02x, use: %s, idx: %02d, ", + a2d_count++, + A2D_SBC_CRC_IDX, p_pkt[A2D_SBC_CRC_IDX], A2D_SBC_USE_MASK, + (p_cur->use)?"cur":"lst", p_cur->idx); + */ + /* descramble */ + idx = a2d_sbc_ds_cb.fr[a2d_sbc_ds_cb.index].idx; + if(idx > 0) + { + p_pkt = &p_pkt[a2d_sbc_ds_cb.base]; + if((idx&1) && (len > (a2d_sbc_ds_cb.base+(idx<<1)))) + { + tmp2 = (idx<<1); + tmp = p_pkt[idx]; + p_pkt[idx] = p_pkt[tmp2]; + p_pkt[tmp2] = tmp; + /* + printf("tmp2: %02d, len: %d, idx: %d\n", + tmp2, len, a2d_sbc_ds_cb.fr[a2d_sbc_ds_cb.index].idx); + */ + } + else + { + tmp2 = p_pkt[idx]; + tmp = (tmp2>>3)+(tmp2<<5); + p_pkt[idx] = (UINT8)tmp; + /* + printf("tmp: x%02x, len: %d, idx: %d(cmp:%d)\n", + (UINT8)tmp2, len, a2d_sbc_ds_cb.fr[a2d_sbc_ds_cb.index].idx, + (a2d_sbc_ds_cb.base+(idx<<1))); + */ + } + } + /* + else + { + printf("!!!!\n"); + } + */ + } +} + +/****************************************************************************** +** +** Function A2D_BldSbcInfo +** +** Description This function is called by an application to build +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** media_type: Indicates Audio, or Multimedia. +** +** p_ie: The SBC Codec Information Element information. +** +** Output Parameters: +** p_result: the resulting codec info byte sequence. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +tA2D_STATUS A2D_BldSbcInfo(UINT8 media_type, tA2D_SBC_CIE *p_ie, UINT8 *p_result) +{ + tA2D_STATUS status; + + if( p_ie == NULL || p_result == NULL || + (p_ie->samp_freq & ~A2D_SBC_IE_SAMP_FREQ_MSK) || + (p_ie->ch_mode & ~A2D_SBC_IE_CH_MD_MSK) || + (p_ie->block_len & ~A2D_SBC_IE_BLOCKS_MSK) || + (p_ie->num_subbands & ~A2D_SBC_IE_SUBBAND_MSK) || + (p_ie->alloc_mthd & ~A2D_SBC_IE_ALLOC_MD_MSK) || + (p_ie->max_bitpool < p_ie->min_bitpool) || + (p_ie->max_bitpool < A2D_SBC_IE_MIN_BITPOOL) || + (p_ie->max_bitpool > A2D_SBC_IE_MAX_BITPOOL) || + (p_ie->min_bitpool < A2D_SBC_IE_MIN_BITPOOL) || + (p_ie->min_bitpool > A2D_SBC_IE_MAX_BITPOOL) ) + { + /* if any unused bit is set */ + status = A2D_INVALID_PARAMS; + } + else + { + status = A2D_SUCCESS; + *p_result++ = A2D_SBC_INFO_LEN; + *p_result++ = media_type; + *p_result++ = A2D_MEDIA_CT_SBC; + + /* Media Codec Specific Information Element */ + *p_result++ = p_ie->samp_freq | p_ie->ch_mode; + + *p_result++ = p_ie->block_len | p_ie->num_subbands | p_ie->alloc_mthd; + + *p_result++ = p_ie->min_bitpool; + *p_result = p_ie->max_bitpool; + } + return status; +} + +/****************************************************************************** +** +** Function A2D_ParsSbcInfo +** +** Description This function is called by an application to parse +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** p_info: the byte sequence to parse. +** +** for_caps: TRUE, if the byte sequence is for get capabilities response. +** +** Output Parameters: +** p_ie: The SBC Codec Information Element information. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +tA2D_STATUS A2D_ParsSbcInfo(tA2D_SBC_CIE *p_ie, UINT8 *p_info, BOOLEAN for_caps) +{ + tA2D_STATUS status; + UINT8 losc; + UINT8 mt; + + if( p_ie == NULL || p_info == NULL) + status = A2D_INVALID_PARAMS; + else + { + losc = *p_info++; + mt = *p_info++; + /* If the function is called for the wrong Media Type or Media Codec Type */ + if(losc != A2D_SBC_INFO_LEN || *p_info != A2D_MEDIA_CT_SBC) + status = A2D_WRONG_CODEC; + else + { + p_info++; + p_ie->samp_freq = *p_info & A2D_SBC_IE_SAMP_FREQ_MSK; + p_ie->ch_mode = *p_info & A2D_SBC_IE_CH_MD_MSK; + p_info++; + p_ie->block_len = *p_info & A2D_SBC_IE_BLOCKS_MSK; + p_ie->num_subbands = *p_info & A2D_SBC_IE_SUBBAND_MSK; + p_ie->alloc_mthd = *p_info & A2D_SBC_IE_ALLOC_MD_MSK; + p_info++; + p_ie->min_bitpool = *p_info++; + p_ie->max_bitpool = *p_info; + status = A2D_SUCCESS; + if(p_ie->min_bitpool < A2D_SBC_IE_MIN_BITPOOL || p_ie->min_bitpool > A2D_SBC_IE_MAX_BITPOOL ) + status = A2D_BAD_MIN_BITPOOL; + + if(p_ie->max_bitpool < A2D_SBC_IE_MIN_BITPOOL || p_ie->max_bitpool > A2D_SBC_IE_MAX_BITPOOL || + p_ie->max_bitpool < p_ie->min_bitpool) + status = A2D_BAD_MAX_BITPOOL; + + if(for_caps == FALSE) + { + if(A2D_BitsSet(p_ie->samp_freq) != A2D_SET_ONE_BIT) + status = A2D_BAD_SAMP_FREQ; + if(A2D_BitsSet(p_ie->ch_mode) != A2D_SET_ONE_BIT) + status = A2D_BAD_CH_MODE; + if(A2D_BitsSet(p_ie->block_len) != A2D_SET_ONE_BIT) + status = A2D_BAD_BLOCK_LEN; + if(A2D_BitsSet(p_ie->num_subbands) != A2D_SET_ONE_BIT) + status = A2D_BAD_SUBBANDS; + if(A2D_BitsSet(p_ie->alloc_mthd) != A2D_SET_ONE_BIT) + status = A2D_BAD_ALLOC_MTHD; + } + } + } + return status; +} + +/****************************************************************************** +** +** Function A2D_BldSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Output Parameters: +** p_dst: the resulting media payload header byte sequence. +** +** Returns void. +******************************************************************************/ +void A2D_BldSbcMplHdr(UINT8 *p_dst, BOOLEAN frag, BOOLEAN start, BOOLEAN last, UINT8 num) +{ + if(p_dst) + { + *p_dst = 0; + if(frag) + *p_dst |= A2D_SBC_HDR_F_MSK; + if(start) + *p_dst |= A2D_SBC_HDR_S_MSK; + if(last) + *p_dst |= A2D_SBC_HDR_L_MSK; + *p_dst |= (A2D_SBC_HDR_NUM_MSK & num); + } +} + +/****************************************************************************** +** +** Function A2D_ParsSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** p_src: the byte sequence to parse.. +** +** Output Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Returns void. +******************************************************************************/ +void A2D_ParsSbcMplHdr(UINT8 *p_src, BOOLEAN *p_frag, BOOLEAN *p_start, BOOLEAN *p_last, UINT8 *p_num) +{ + if(p_src && p_frag && p_start && p_last && p_num) + { + *p_frag = (*p_src & A2D_SBC_HDR_F_MSK) ? TRUE: FALSE; + *p_start= (*p_src & A2D_SBC_HDR_S_MSK) ? TRUE: FALSE; + *p_last = (*p_src & A2D_SBC_HDR_L_MSK) ? TRUE: FALSE; + *p_num = (*p_src & A2D_SBC_HDR_NUM_MSK); + } +} + +#endif /* A2D_SBC_INCLUDED == TRUE */ diff --git a/stack/avct/avct_api.c b/stack/avct/avct_api.c new file mode 100644 index 0000000..7e37a90 --- /dev/null +++ b/stack/avct/avct_api.c @@ -0,0 +1,465 @@ +/****************************************************************************** + * + * 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 module contains API of the audio/video control transport protocol. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "gki.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "btm_api.h" +#include "avct_api.h" +#include "avct_int.h" + +/* Control block for AVCT */ +#if AVCT_DYNAMIC_MEMORY == FALSE +tAVCT_CB avct_cb; +#endif + +/******************************************************************************* +** +** Function AVCT_Register +** +** Description This is the system level registration function for the +** AVCTP protocol. This function initializes AVCTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVCTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask) +{ + AVCT_TRACE_API0("AVCT_Register"); + + /* register PSM with L2CAP */ + L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_appl); + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + + /* initialize AVCTP data structures */ + memset(&avct_cb, 0, sizeof(tAVCT_CB)); + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* Include the browsing channel which uses eFCR */ + L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_br_appl); + + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + + if (mtu_br < AVCT_MIN_BROWSE_MTU) + mtu_br = AVCT_MIN_BROWSE_MTU; + avct_cb.mtu_br = mtu_br; +#endif + +#if defined(AVCT_INITIAL_TRACE_LEVEL) + avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL; +#else + avct_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + + if (mtu < AVCT_MIN_CONTROL_MTU) + mtu = AVCT_MIN_CONTROL_MTU; + /* store mtu */ + avct_cb.mtu = mtu; +} + +/******************************************************************************* +** +** Function AVCT_Deregister +** +** Description This function is called to deregister use AVCTP protocol. +** It is called when AVCTP is no longer being used by any +** application in the system. Before this function can be +** called, all connections must be removed with +** AVCT_RemoveConn(). +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Deregister(void) +{ + AVCT_TRACE_API0("AVCT_Deregister"); + + /* deregister PSM with L2CAP */ + L2CA_Deregister(AVCT_PSM); +} + +/******************************************************************************* +** +** Function AVCT_CreateConn +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, BD_ADDR peer_addr) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_LCB *p_lcb; + + AVCT_TRACE_API2("AVCT_CreateConn: %d, control:%d", p_cc->role, p_cc->control); + + /* Allocate ccb; if no ccbs, return failure */ + if ((p_ccb = avct_ccb_alloc(p_cc)) == NULL) + { + result = AVCT_NO_RESOURCES; + } + else + { + /* get handle */ + *p_handle = avct_ccb_to_idx(p_ccb); + + /* if initiator connection */ + if (p_cc->role == AVCT_INT) + { + /* find link; if none allocate a new one */ + if ((p_lcb = avct_lcb_by_bd(peer_addr)) == NULL) + { + if ((p_lcb = avct_lcb_alloc(peer_addr)) == NULL) + { + /* no link resources; free ccb as well */ + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_NO_RESOURCES; + } + } + /* check if PID already in use */ + else if (avct_lcb_has_pid(p_lcb, p_cc->pid)) + { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_PID_IN_USE; + } + + if (result == AVCT_SUCCESS) + { + /* bind lcb to ccb */ + p_ccb->p_lcb = p_lcb; + AVCT_TRACE_DEBUG1("ch_state: %d", p_lcb->ch_state); + avct_lcb_event(p_lcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_RemoveConn +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveConn(UINT8 handle) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API0("AVCT_RemoveConn"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + } + /* if connection not bound to lcb, dealloc */ + else if (p_ccb->p_lcb == NULL) + { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + } + /* send unbind event to lcb */ + else + { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_CreateBrowse +** +** Description Create an AVCTP Browse channel. There are two types of +** connections, initiator and acceptor, as determined by +** the role parameter. When this function is called to +** create an initiator connection, the Browse channel to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateBrowse (UINT8 handle, UINT8 role) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_BCB *p_bcb; + int index; + + AVCT_TRACE_API1("AVCT_CreateBrowse: %d", role); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + return AVCT_BAD_HANDLE; + } + else + { + /* mark this CCB as supporting browsing channel */ + if ((p_ccb->allocated & AVCT_ALOC_BCB) == 0) + { + p_ccb->allocated |= AVCT_ALOC_BCB; + } + } + + /* if initiator connection */ + if (role == AVCT_INT) + { + /* the link control block must exist before this function is called as INT. */ + if (p_ccb->p_lcb == NULL) + { + result = AVCT_NOT_OPEN; + } + else + { + /* find link; if none allocate a new one */ + index = p_ccb->p_lcb->allocated; + if (index > AVCT_NUM_LINKS) + { + result = AVCT_BAD_HANDLE; + } + else + { + p_bcb = &avct_cb.bcb[index - 1]; + p_bcb->allocated = index; + } + } + + if (result == AVCT_SUCCESS) + { + /* bind bcb to ccb */ + p_ccb->p_bcb = p_bcb; + AVCT_TRACE_DEBUG1("ch_state: %d", p_bcb->ch_state); + avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + + return result; +#else + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_RemoveBrowse +** +** Description Remove an AVCTP Browse channel. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveBrowse (UINT8 handle) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API0("AVCT_RemoveBrowse"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + } + else if (p_ccb->p_bcb != NULL) + /* send unbind event to bcb */ + { + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +#else + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_GetBrowseMtu +** +** Description Get the peer_mtu for the AVCTP Browse channel of the given +** connection. +** +** Returns the peer browsing channel MTU. +** +*******************************************************************************/ +UINT16 AVCT_GetBrowseMtu (UINT8 handle) +{ + UINT16 peer_mtu = AVCT_MIN_BROWSE_MTU; +#if (AVCT_BROWSE_INCLUDED == TRUE) + tAVCT_CCB *p_ccb; + + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL && p_ccb->p_bcb != NULL) + { + peer_mtu = p_ccb->p_bcb->peer_mtu; + } +#endif + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_GetPeerMtu +** +** Description Get the peer_mtu for the AVCTP channel of the given +** connection. +** +** Returns the peer MTU size. +** +*******************************************************************************/ +UINT16 AVCT_GetPeerMtu (UINT8 handle) +{ + UINT16 peer_mtu = L2CAP_DEFAULT_MTU; + tAVCT_CCB *p_ccb; + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL) + { + if (p_ccb->p_lcb) + { + peer_mtu = p_ccb->p_lcb->peer_mtu; + } + } + + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_MsgReq +** +** Description Send an AVCTP message to a peer device. In calling +** AVCT_MsgReq(), the application should keep track of the +** congestion state of AVCTP as communicated with events +** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the +** application calls AVCT_MsgReq() when AVCTP is congested +** the message may be discarded. The application may make its +** first call to AVCT_MsgReq() after it receives an +** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or +** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel. +** +** p_msg->layer_specific must be set to +** AVCT_DATA_CTRL for control channel traffic; +** AVCT_DATA_BROWSE for for browse channel traffic. +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_UL_MSG ul_msg; + + AVCT_TRACE_API0("AVCT_MsgReq"); + + /* verify p_msg parameter */ + if (p_msg == NULL) + { + return AVCT_NO_RESOURCES; + } + AVCT_TRACE_API1("len: %d", p_msg->len); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + GKI_freebuf(p_msg); + } + /* verify channel is bound to link */ + else if (p_ccb->p_lcb == NULL) + { + result = AVCT_NOT_OPEN; + GKI_freebuf(p_msg); + } + + if (result == AVCT_SUCCESS) + { + ul_msg.p_buf = p_msg; + ul_msg.p_ccb = p_ccb; + ul_msg.label = label; + ul_msg.cr = cr; + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* send msg event to bcb */ + if (p_msg->layer_specific == AVCT_DATA_BROWSE) + { + if (p_ccb->p_bcb == NULL && (p_ccb->allocated & AVCT_ALOC_BCB) == 0) + { + /* BCB channel is not open and not allocated */ + result = AVCT_BAD_HANDLE; + GKI_freebuf(p_msg); + } + else + { + p_ccb->p_bcb = avct_bcb_by_lcb(p_ccb->p_lcb); + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + /* send msg event to lcb */ + else +#endif + { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + return result; +} + diff --git a/stack/avct/avct_ccb.c b/stack/avct/avct_ccb.c new file mode 100644 index 0000000..09051b1 --- /dev/null +++ b/stack/avct/avct_ccb.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * + * 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 module contains functions which operate on the AVCTP connection + * control block. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" + +/******************************************************************************* +** +** Function avct_ccb_alloc +** +** Description Allocate a connection control block; copy parameters to ccb. +** +** +** Returns pointer to the ccb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (!p_ccb->allocated) + { + p_ccb->allocated = AVCT_ALOC_LCB; + memcpy(&p_ccb->cc, p_cc, sizeof(tAVCT_CC)); + AVCT_TRACE_DEBUG1("avct_ccb_alloc %d", i); + break; + } + } + + if (i == AVCT_NUM_CONN) + { + /* out of ccbs */ + p_ccb = NULL; + AVCT_TRACE_WARNING0("Out of ccbs"); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avct_ccb_dealloc +** +** Description Deallocate a connection control block and call application +** callback. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr) +{ + tAVCT_CTRL_CBACK *p_cback = p_ccb->cc.p_ctrl_cback; + + AVCT_TRACE_DEBUG1("avct_ccb_dealloc %d", avct_ccb_to_idx(p_ccb)); +#if (AVCT_BROWSE_INCLUDED == TRUE) + if(p_ccb->p_bcb == NULL) + memset(p_ccb, 0, sizeof(tAVCT_CCB)); + else + { + /* control channel is down, but the browsing channel is still connected 0 disconnect it now */ + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + p_ccb->p_lcb = NULL; + } +#else + memset(p_ccb, 0, sizeof(tAVCT_CCB)); +#endif + + if (event != AVCT_NO_EVT) + { + (*p_cback)(avct_ccb_to_idx(p_ccb), event, result, bd_addr); + } +} + +/******************************************************************************* +** +** Function avct_ccb_to_idx +** +** Description Given a pointer to an ccb, return its index. +** +** +** Returns Index of ccb. +** +*******************************************************************************/ +UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb) +{ + /* use array arithmetic to determine index */ + return (UINT8) (p_ccb - avct_cb.ccb); +} + +/******************************************************************************* +** +** Function avct_ccb_by_idx +** +** Description Return ccb pointer based on ccb index (or handle). +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_by_idx(UINT8 idx) +{ + tAVCT_CCB *p_ccb; + + /* verify index */ + if (idx < AVCT_NUM_CONN) + { + p_ccb = &avct_cb.ccb[idx]; + + /* verify ccb is allocated */ + if (!p_ccb->allocated) + { + p_ccb = NULL; + AVCT_TRACE_WARNING1("ccb %d not allocated", idx); + } + } + else + { + p_ccb = NULL; + AVCT_TRACE_WARNING1("No ccb for idx %d", idx); + } + return p_ccb; +} diff --git a/stack/avct/avct_defs.h b/stack/avct/avct_defs.h new file mode 100644 index 0000000..30b8859 --- /dev/null +++ b/stack/avct/avct_defs.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * 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 contains constants definitions and other information from the AVCTP + * specification. This file is intended for use internal to AVCT only. + * + ******************************************************************************/ +#ifndef AVCT_DEFS_H +#define AVCT_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* packet type */ +#define AVCT_PKT_TYPE_SINGLE 0 /* single packet */ +#define AVCT_PKT_TYPE_START 1 /* start packet */ +#define AVCT_PKT_TYPE_CONT 2 /* continue packet */ +#define AVCT_PKT_TYPE_END 3 /* end packet */ + +/* header lengths for different packet types */ +#define AVCT_HDR_LEN_SINGLE 3 +#define AVCT_HDR_LEN_START 4 +#define AVCT_HDR_LEN_CONT 1 +#define AVCT_HDR_LEN_END 1 + +/* invalid cr+ipid value */ +#define AVCT_CR_IPID_INVALID 1 + +/***************************************************************************** +** message parsing and building macros +*****************************************************************************/ + +#define AVCT_BLD_HDR(p, label, type, cr_ipid) \ + *(p)++ = ((label) << 4) | ((type) << 2) | (cr_ipid); + +#define AVCT_PRS_HDR(p, label, type, cr_ipid) \ + label = *(p) >> 4; \ + type = (*(p) >> 2) & 3; \ + cr_ipid = *(p)++ & 3; + +#define AVCT_PRS_PKT_TYPE(p, type) \ + type = (*(p) >> 2) & 3; + +#endif /* AVCT_DEFS_H */ diff --git a/stack/avct/avct_int.h b/stack/avct/avct_int.h new file mode 100644 index 0000000..b93c032 --- /dev/null +++ b/stack/avct/avct_int.h @@ -0,0 +1,239 @@ +/****************************************************************************** + * + * 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 file contains interfaces which are internal to AVCTP. + * + ******************************************************************************/ +#ifndef AVCT_INT_H +#define AVCT_INT_H + +#include "gki.h" +#include "avct_api.h" +#include "avct_defs.h" +#include "l2c_api.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* lcb state machine events */ +enum { + AVCT_LCB_UL_BIND_EVT, + AVCT_LCB_UL_UNBIND_EVT, + AVCT_LCB_UL_MSG_EVT, + AVCT_LCB_INT_CLOSE_EVT, + AVCT_LCB_LL_OPEN_EVT, + AVCT_LCB_LL_CLOSE_EVT, + AVCT_LCB_LL_MSG_EVT, + AVCT_LCB_LL_CONG_EVT +}; + + +/* "states" used for L2CAP channel */ +#define AVCT_CH_IDLE 0 /* No connection */ +#define AVCT_CH_CONN 1 /* Waiting for connection confirm */ +#define AVCT_CH_CFG 2 /* Waiting for configuration complete */ +#define AVCT_CH_OPEN 3 /* Channel opened */ + +/* "no event" indicator used by ccb dealloc */ +#define AVCT_NO_EVT 0xFF + +/***************************************************************************** +** data types +*****************************************************************************/ +/* sub control block type - common data members for tAVCT_LCB and tAVCT_BCB */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ +} tAVCT_SCB; + +/* link control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_rx_msg; /* Message being reassembled */ + UINT16 conflict_lcid; /* L2CAP channel LCID */ + BD_ADDR peer_addr; /* BD address of peer */ + BUFFER_Q tx_q; /* Transmit data buffer queue */ + BOOLEAN cong; /* TRUE, if congested */ +} tAVCT_LCB; + +/* browse control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_tx_msg; /* Message to be sent - in case the browsing channel is not open when MsgReg is called */ + UINT8 ch_close; /* CCB index+1, if CCB initiated channel close */ +} tAVCT_BCB; + +#define AVCT_ALOC_LCB 0x01 +#define AVCT_ALOC_BCB 0x02 +/* connection control block */ +typedef struct { + tAVCT_CC cc; /* parameters from connection creation */ + tAVCT_LCB *p_lcb; /* Associated LCB */ + tAVCT_BCB *p_bcb; /* associated BCB */ + BOOLEAN ch_close; /* Whether CCB initiated channel close */ + UINT8 allocated; /* Whether LCB/BCB is allocated */ +} tAVCT_CCB; + +/* data type associated with UL_MSG_EVT */ +typedef struct { + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT8 label; + UINT8 cr; +} tAVCT_UL_MSG; + +/* union associated with lcb state machine events */ +typedef union { + tAVCT_UL_MSG ul_msg; + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT16 result; + BOOLEAN cong; + UINT8 err_code; +} tAVCT_LCB_EVT; + +/* Control block for AVCT */ +typedef struct { + tAVCT_LCB lcb[AVCT_NUM_LINKS]; /* link control blocks */ + tAVCT_BCB bcb[AVCT_NUM_LINKS]; /* browse control blocks */ + tAVCT_CCB ccb[AVCT_NUM_CONN]; /* connection control blocks */ + UINT16 mtu; /* our L2CAP MTU */ + UINT16 mtu_br; /* our L2CAP MTU for the Browsing channel */ + UINT8 trace_level; /* trace level */ +} tAVCT_CB; + +/***************************************************************************** +** function declarations +*****************************************************************************/ + +/* LCB function declarations */ +extern void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data); +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data); +extern void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb); +extern tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb); +extern BOOLEAN avct_bcb_last_ccb(tAVCT_BCB *p_bcb, tAVCT_CCB *p_ccb_last); +extern tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid); +#endif +extern tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr); +extern tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr); +extern void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid); +extern tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid); +extern BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last); + +/* LCB action functions */ +extern void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); + +/* BCB action functions */ +#if (AVCT_BROWSE_INCLUDED == TRUE) +typedef void (*tAVCT_BCB_ACTION)(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern const tAVCT_BCB_ACTION avct_bcb_action[]; +extern const UINT8 avct_lcb_pkt_type_len[]; +extern const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def; +#endif + +/* CCB function declarations */ +extern tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc); +extern void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr); +extern UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb); +extern tAVCT_CCB *avct_ccb_by_idx(UINT8 idx); + + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Main control block */ +#if AVCT_DYNAMIC_MEMORY == FALSE +AVCT_API extern tAVCT_CB avct_cb; +#else +AVCT_API extern tAVCT_CB *avct_cb_ptr; +#define avct_cb (*avct_cb_ptr) +#endif + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO avct_l2c_appl; +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern const tL2CAP_APPL_INFO avct_l2c_br_appl; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AVCT_INT_H */ + + diff --git a/stack/avct/avct_l2c.c b/stack/avct/avct_l2c.c new file mode 100644 index 0000000..ef0f5fc --- /dev/null +++ b/stack/avct/avct_l2c.c @@ -0,0 +1,434 @@ +/****************************************************************************** + * + * 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 AVCTP module interfaces to L2CAP + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" + +/* Configuration flags. */ +#define AVCT_L2C_CFG_IND_DONE (1<<0) +#define AVCT_L2C_CFG_CFM_DONE (1<<1) + +/* callback function declarations */ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avct_l2c_appl = { + avct_l2c_connect_ind_cback, + avct_l2c_connect_cfm_cback, + NULL, + avct_l2c_config_ind_cback, + avct_l2c_config_cfm_cback, + avct_l2c_disconnect_ind_cback, + avct_l2c_disconnect_cfm_cback, + NULL, + avct_l2c_data_ind_cback, + avct_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avct_l2c_is_passive +** +** Description check is the CCB associated with the given LCB was created +** as passive +** +** Returns TRUE, if the given LCB is created as AVCT_PASSIVE +** +*******************************************************************************/ +static BOOLEAN avct_l2c_is_passive (tAVCT_LCB *p_lcb) +{ + BOOLEAN is_passive = FALSE; + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + AVCT_TRACE_DEBUG1("avct_l2c_is_ct control:x%x", p_ccb->cc.control); + if (p_ccb->cc.control & AVCT_PASSIVE) + { + is_passive = TRUE; + break; + } + } + } + return is_passive; +} + +/******************************************************************************* +** +** Function avct_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVCT_LCB *p_lcb; + UINT16 result = L2CAP_CONN_OK; + tL2CAP_CFG_INFO cfg; + + /* do we already have a channel for this peer? */ + if ((p_lcb = avct_lcb_by_bd(bd_addr)) == NULL) + { + /* no, allocate lcb */ + if ((p_lcb = avct_lcb_alloc(bd_addr)) == NULL) + { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + } + /* else we already have a channel for this peer */ + else + { + if (!avct_l2c_is_passive (p_lcb) || (p_lcb->ch_state == AVCT_CH_OPEN)) + { + /* this LCB included CT role - reject */ + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* TG role only - accept the connection from CT. move the channel ID to the conflict list */ + p_lcb->conflict_lcid = p_lcb->ch_lcid; + AVCT_TRACE_DEBUG1("avct_l2c_connect_ind_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + } + } + + if(p_lcb) + { + AVCT_TRACE_DEBUG3("avct_l2c_connect_ind_cback: 0x%x, res: %d, ch_state: %d", + lcid, result, p_lcb->ch_state); + } + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store LCID */ + p_lcb->ch_lcid = lcid; + + /* transition to configuration state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req"); + } + +#if (BT_USE_TRACES == TRUE) + if(p_lcb) + AVCT_TRACE_DEBUG1("ch_state cni: %d ", p_lcb->ch_state); +#endif +} + +/******************************************************************************* +** +** Function avct_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + tL2CAP_CFG_INFO cfg; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG4("avct_l2c_connect_cfm_cback lcid:0x%x result: %d ch_state: %d, conflict_lcid:0x%x", + lcid, result, p_lcb->ch_state, p_lcb->conflict_lcid); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req"); + } + /* else failure */ + else + { + AVCT_TRACE_DEBUG1("avct_l2c_connect_cfm_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + if (p_lcb->conflict_lcid == lcid) + p_lcb->conflict_lcid = 0; + else + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } + } + else if (p_lcb->conflict_lcid == lcid) + { + /* we must be in AVCT_CH_CFG state for the ch_lcid channel */ + AVCT_TRACE_DEBUG2("avct_l2c_connect_cfm_cback ch_state: %d, conflict_lcid:0x%x", p_lcb->ch_state, p_lcb->conflict_lcid); + if (result == L2CAP_CONN_OK) + { + /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + p_lcb->conflict_lcid = 0; + } + AVCT_TRACE_DEBUG1("ch_state cnc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG3("avct_l2c_config_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, p_cfg->result); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + /* else failure */ + else + { + AVCT_TRACE_DEBUG1("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ", p_lcb->ch_state); + /* store result value */ + p_lcb->ch_result = p_cfg->result; + + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + AVCT_TRACE_DEBUG1("ch_state cfc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG2("avct_l2c_config_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_lcb->peer_mtu = p_cfg->mtu; + } + else + { + p_lcb->peer_mtu = L2CAP_DEFAULT_MTU; + } + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_CFM_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + AVCT_TRACE_DEBUG1("ch_state cfi: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVCT_LCB *p_lcb; + UINT16 result = AVCT_RESULT_FAIL; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG2("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + AVCT_TRACE_DEBUG1("ch_state di: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + UINT16 res; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG3("avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, result); + /* result value may be previously stored */ + res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result; + p_lcb->ch_result = 0; + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &res); + AVCT_TRACE_DEBUG1("ch_state dc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG1("avct_l2c_congestion_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + avct_lcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested); + } +} + +/******************************************************************************* +** +** Function avct_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG1("avct_l2c_data_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + avct_lcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf); + } + else /* prevent buffer leak */ + { + AVCT_TRACE_WARNING0("ERROR -> avct_l2c_data_ind_cback drop buffer"); + GKI_freebuf(p_buf); + } +} + diff --git a/stack/avct/avct_lcb.c b/stack/avct/avct_lcb.c new file mode 100644 index 0000000..bb3f447 --- /dev/null +++ b/stack/avct/avct_lcb.c @@ -0,0 +1,471 @@ +/****************************************************************************** + * + * 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 module contains the link control state machine and functions which + * operate on the link control block. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "gki.h" + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ + +#if BT_TRACE_VERBOSE == TRUE + +/* verbose state strings for trace */ +const char * const avct_lcb_st_str[] = { + "LCB_IDLE_ST", + "LCB_OPENING_ST", + "LCB_OPEN_ST", + "LCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char * const avct_lcb_evt_str[] = { + "UL_BIND_EVT", + "UL_UNBIND_EVT", + "UL_MSG_EVT", + "INT_CLOSE_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_MSG_EVT", + "LL_CONG_EVT" +}; + +#endif + +/* lcb state machine states */ +enum { + AVCT_LCB_IDLE_ST, + AVCT_LCB_OPENING_ST, + AVCT_LCB_OPEN_ST, + AVCT_LCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVCT_LCB_CHNL_OPEN, + AVCT_LCB_CHNL_DISC, + AVCT_LCB_SEND_MSG, + AVCT_LCB_OPEN_IND, + AVCT_LCB_OPEN_FAIL, + AVCT_LCB_CLOSE_IND, + AVCT_LCB_CLOSE_CFM, + AVCT_LCB_MSG_IND, + AVCT_LCB_CONG_IND, + AVCT_LCB_BIND_CONN, + AVCT_LCB_BIND_FAIL, + AVCT_LCB_UNBIND_DISC, + AVCT_LCB_CHK_DISC, + AVCT_LCB_DISCARD_MSG, + AVCT_LCB_DEALLOC, + AVCT_LCB_FREE_MSG_IND, + AVCT_LCB_NUM_ACTIONS +}; + +#define AVCT_LCB_IGNORE AVCT_LCB_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tAVCT_LCB_ACTION)(tAVCT_LCB *p_ccb, tAVCT_LCB_EVT *p_data); + +/* action function list */ +const tAVCT_LCB_ACTION avct_lcb_action[] = { + avct_lcb_chnl_open, + avct_lcb_chnl_disc, + avct_lcb_send_msg, + avct_lcb_open_ind, + avct_lcb_open_fail, + avct_lcb_close_ind, + avct_lcb_close_cfm, + avct_lcb_msg_ind, + avct_lcb_cong_ind, + avct_lcb_bind_conn, + avct_lcb_bind_fail, + avct_lcb_unbind_disc, + avct_lcb_chk_disc, + avct_lcb_discard_msg, + avct_lcb_dealloc, + avct_lcb_free_msg_ind +}; + +/* state table information */ +#define AVCT_LCB_ACTIONS 2 /* number of actions */ +#define AVCT_LCB_NEXT_STATE 2 /* position of next state */ +#define AVCT_LCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avct_lcb_st_idle[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_CHNL_OPEN, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avct_lcb_st_opening[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_OPEN_FAIL, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avct_lcb_st_open[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_BIND_CONN, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_CHK_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_SEND_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 avct_lcb_st_closing[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_BIND_FAIL, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_CFM, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVCT_LCB_ST_TBL)[AVCT_LCB_NUM_COLS]; + +/* state table */ +const tAVCT_LCB_ST_TBL avct_lcb_st_tbl[] = { + avct_lcb_st_idle, + avct_lcb_st_opening, + avct_lcb_st_open, + avct_lcb_st_closing +}; + +/******************************************************************************* +** +** Function avct_lcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT3("LCB lcb=%d event=%s state=%s", p_lcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_lcb->state]); +#else + AVCT_TRACE_EVENT3("LCB lcb=%d event=%d state=%d", p_lcb->allocated, event, p_lcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_lcb->state]; + + /* set next state */ + p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) + { + (*avct_lcb_action[action])(p_lcb, p_data); + } + else + { + break; + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +#if (AVCT_BROWSE_INCLUDED == TRUE) +void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT3("BCB lcb=%d event=%s state=%s", p_bcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_bcb->state]); +#else + AVCT_TRACE_EVENT3("BCB lcb=%d event=%d state=%d", p_bcb->allocated, event, p_bcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_bcb->state]; + + /* set next state */ + p_bcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) + { + (*avct_bcb_action[action])(p_bcb, p_data); + } + else + { + break; + } + } +} +#endif + +/******************************************************************************* +** +** Function avct_lcb_by_bd +** +** Description This lookup function finds the lcb for a BD address. +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + /* if allocated lcb has matching lcb */ + if (p_lcb->allocated && (!memcmp(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN))) + { + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* if no lcb found */ + p_lcb = NULL; + + AVCT_TRACE_DEBUG6("No lcb for addr %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_alloc +** +** Description Allocate a link control block. +** +** +** Returns pointer to the lcb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + if (!p_lcb->allocated) + { + p_lcb->allocated = (UINT8)(i + 1); + memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN); + AVCT_TRACE_DEBUG1("avct_lcb_alloc %d", p_lcb->allocated); + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING0("Out of lcbs"); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_dealloc +** +** Description Deallocate a link control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + BOOLEAN found = FALSE; + int i; + + AVCT_TRACE_DEBUG1("avct_lcb_dealloc %d", p_lcb->allocated); + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + if (p_ccb->p_lcb == p_lcb) + { + AVCT_TRACE_DEBUG1("avct_lcb_dealloc used by ccb: %d", i); + found = TRUE; + break; + } + } + } + + if (!found) + { + AVCT_TRACE_DEBUG0("avct_lcb_dealloc now"); + + /* clear reassembled msg buffer if in use */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + } + memset(p_lcb, 0, sizeof(tAVCT_LCB)); + } +} + +/******************************************************************************* +** +** Function avct_lcb_by_lcid +** +** Description Find the LCB associated with the L2CAP LCID +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + if (p_lcb->allocated && ((p_lcb->ch_lcid == lcid) || (p_lcb->conflict_lcid == lcid))) + { + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING1("No lcb for lcid %x", lcid); + } + + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_has_pid +** +** Description See if any ccbs on this lcb have a particular pid. +** +** +** Returns Pointer to CCB if PID found, NULL otherwise. +** +*******************************************************************************/ +tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb->cc.pid == pid)) + { + return p_ccb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function avct_lcb_last_ccb +** +** Description See if given ccb is only one on the lcb. +** +** +** Returns TRUE if ccb is last, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + AVCT_TRACE_WARNING0("avct_lcb_last_ccb"); + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + AVCT_TRACE_WARNING6("%x: aloc:%d, lcb:0x%x/0x%x, ccb:0x%x/0x%x", + i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last); + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) + { + return FALSE; + } + } + return TRUE; +} + + + diff --git a/stack/avct/avct_lcb_act.c b/stack/avct/avct_lcb_act.c new file mode 100644 index 0000000..b883517 --- /dev/null +++ b/stack/avct/avct_lcb_act.c @@ -0,0 +1,702 @@ +/****************************************************************************** + * + * 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 module contains action functions of the link control state machine. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "gki.h" +#include "btm_api.h" + +/* packet header length lookup table */ +const UINT8 avct_lcb_pkt_type_len[] = { + AVCT_HDR_LEN_SINGLE, + AVCT_HDR_LEN_START, + AVCT_HDR_LEN_CONT, + AVCT_HDR_LEN_END +}; + +/******************************************************************************* +** +** Function avct_lcb_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +static BT_HDR *avct_lcb_msg_asmbl(tAVCT_LCB *p_lcb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_PRS_PKT_TYPE(p, pkt_type); + + /* quick sanity check on length */ + if (p_buf->len < avct_lcb_pkt_type_len[pkt_type]) + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING0("Bad length during reassembly"); + p_ret = NULL; + } + /* single packet */ + else if (pkt_type == AVCT_PKT_TYPE_SINGLE) + { + /* if reassembly in progress drop message and process new single */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + AVCT_TRACE_WARNING0("Got single during reassembly"); + } + p_ret = p_buf; + } + /* start packet */ + else if (pkt_type == AVCT_PKT_TYPE_START) + { + /* if reassembly in progress drop message and process new start */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + AVCT_TRACE_WARNING0("Got start during reassembly"); + } + p_lcb->p_rx_msg = p_buf; + + /* copy first header byte over nosp */ + *(p + 1) = *p; + + /* set offset to point to where to copy next */ + p_lcb->p_rx_msg->offset += p_lcb->p_rx_msg->len; + + /* adjust length for packet header */ + p_lcb->p_rx_msg->len -= 1; + + p_ret = NULL; + } + /* continue or end */ + else + { + /* if no reassembly in progress drop message */ + if (p_lcb->p_rx_msg == NULL) + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING1("Pkt type=%d out of order", pkt_type); + p_ret = NULL; + } + else + { + /* get size of buffer holding assembled message */ + buf_len = GKI_get_buf_size(p_lcb->p_rx_msg) - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVCT_HDR_LEN_CONT; + p_buf->len -= AVCT_HDR_LEN_CONT; + + /* verify length */ + if ((p_lcb->p_rx_msg->offset + p_buf->len) > buf_len) + { + /* won't fit; free everything */ + GKI_freebuf(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + GKI_freebuf(p_buf); + p_ret = NULL; + AVCT_TRACE_WARNING0("Fragmented message to big!"); + } + else + { + /* copy contents of p_buf to p_rx_msg */ + memcpy((UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + if (pkt_type == AVCT_PKT_TYPE_END) + { + p_lcb->p_rx_msg->offset -= p_lcb->p_rx_msg->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = p_lcb->p_rx_msg; + p_lcb->p_rx_msg = NULL; + } + else + { + p_lcb->p_rx_msg->offset += p_buf->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = NULL; + } + GKI_freebuf(p_buf); + } + } + } + return p_ret; +} + + +/******************************************************************************* +** +** Function avct_lcb_chnl_open +** +** Description Open L2CAP channel to peer +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 result = AVCT_RESULT_FAIL; + + BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP, 0); + /* call l2cap connect req */ + p_lcb->ch_state = AVCT_CH_CONN; + if ((p_lcb->ch_lcid = L2CA_ConnectReq(AVCT_PSM, p_lcb->peer_addr)) == 0) + { + /* if connect req failed, send ourselves close event */ + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } +} + +/******************************************************************************* +** +** Function avct_lcb_unbind_disc +** +** Description Deallocate ccb and call callback with disconnect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + avct_ccb_dealloc(p_data->p_ccb, AVCT_DISCONNECT_CFM_EVT, 0, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_open_ind +** +** Description Handle an LL_OPEN event. For each allocated ccb already +** bound to this lcb, send a connect event. For each +** unbound ccb with a new PID, bind that ccb to this lcb and +** send a connect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + BOOLEAN bind = FALSE; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + /* if bound to this lcb send connect confirm event */ + if (p_ccb->p_lcb == p_lcb) + { + bind = TRUE; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_CFM_EVT, + 0, p_lcb->peer_addr); + } + /* if unbound acceptor and lcb doesn't already have a ccb for this PID */ + else if ((p_ccb->p_lcb == NULL) && (p_ccb->cc.role == AVCT_ACP) && + (avct_lcb_has_pid(p_lcb, p_ccb->cc.pid) == NULL)) + { + /* bind ccb to lcb and send connect ind event */ + bind = TRUE; + p_ccb->p_lcb = p_lcb; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } + + /* if no ccbs bound to this lcb, disconnect */ + if (bind == FALSE) + { + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_open_fail +** +** Description L2CAP channel open attempt failed. Deallocate any ccbs +** on this lcb and send connect confirm event with failure. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + avct_ccb_dealloc(p_ccb, AVCT_CONNECT_CFM_EVT, + p_data->result, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_ind +** +** Description L2CAP channel closed by peer. Deallocate any initiator +** ccbs on this lcb and send disconnect ind event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + if (p_ccb->cc.role == AVCT_INT) + { + avct_ccb_dealloc(p_ccb, AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + else + { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_cfm +** +** Description L2CAP channel closed by us. Deallocate any initiator +** ccbs on this lcb and send disconnect ind or cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + /* if this ccb initiated close send disconnect cfm otherwise ind */ + if (p_ccb->ch_close) + { + p_ccb->ch_close = FALSE; + event = AVCT_DISCONNECT_CFM_EVT; + } + else + { + event = AVCT_DISCONNECT_IND_EVT; + } + + if (p_ccb->cc.role == AVCT_INT) + { + avct_ccb_dealloc(p_ccb, event, p_data->result, p_lcb->peer_addr); + } + else + { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, + p_data->result, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_bind_conn +** +** Description Bind ccb to lcb and send connect cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + p_data->p_ccb->p_lcb = p_lcb; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), + AVCT_CONNECT_CFM_EVT, 0, p_lcb->peer_addr); +} + +/******************************************************************************* +** +** Function avct_lcb_chk_disc +** +** Description A ccb wants to close; if it is the last ccb on this lcb, +** close channel. Otherwise just deallocate and call +** callback. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("avct_lcb_chk_disc"); +#if (AVCT_BROWSE_INCLUDED == TRUE) + avct_close_bcb(p_lcb, p_data); +#endif + if (avct_lcb_last_ccb(p_lcb, p_data->p_ccb)) + { + AVCT_TRACE_WARNING0("closing"); + p_data->p_ccb->ch_close = TRUE; + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } + else + { + AVCT_TRACE_WARNING0("dealloc ccb"); + avct_lcb_unbind_disc(p_lcb, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_chnl_disc +** +** Description Disconnect L2CAP channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + L2CA_DisconnectReq(p_lcb->ch_lcid); +} + +/******************************************************************************* +** +** Function avct_lcb_bind_fail +** +** Description Deallocate ccb and call callback with connect event +** with failure result. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_cong_ind +** +** Description Handle congestion indication from L2CAP. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + BT_HDR *p_buf; + + /* set event */ + event = (p_data->cong) ? AVCT_CONG_IND_EVT : AVCT_UNCONG_IND_EVT; + p_lcb->cong = p_data->cong; + if (p_lcb->cong == FALSE && GKI_getfirst(&p_lcb->tx_q)) + { + while ( !p_lcb->cong && (p_buf = (BT_HDR *)GKI_dequeue(&p_lcb->tx_q)) != NULL) + { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_lcb->cong = TRUE; + } + } + } + + /* send event to all ccbs on this lcb */ + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_discard_msg +** +** Description Discard a message sent in from the API. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("Dropping msg"); + + GKI_freebuf(p_data->ul_msg.p_buf); +} + +/******************************************************************************* +** +** Function avct_lcb_send_msg +** +** Description Build and send an AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 hdr_len; + BT_HDR *p_buf; + UINT8 *p; + UINT8 nosp = 0; /* number of subsequent packets */ + UINT16 temp; + UINT16 buf_size = p_lcb->peer_mtu + L2CAP_MIN_OFFSET + BT_HDR_SIZE; + + + /* store msg len */ + curr_msg_len = p_data->ul_msg.p_buf->len; + + /* initialize packet type and other stuff */ + if (curr_msg_len <= (p_lcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) + { + pkt_type = AVCT_PKT_TYPE_SINGLE; + } + else + { + pkt_type = AVCT_PKT_TYPE_START; + temp = (curr_msg_len + AVCT_HDR_LEN_START - p_lcb->peer_mtu); + nosp = temp / (p_lcb->peer_mtu - 1) + 1; + if ( (temp % (p_lcb->peer_mtu - 1)) != 0) + nosp++; + } + + /* while we haven't sent all packets */ + while (curr_msg_len != 0) + { + /* set header len */ + hdr_len = avct_lcb_pkt_type_len[pkt_type]; + + /* if remaining msg must be fragmented */ + if (p_data->ul_msg.p_buf->len > (p_lcb->peer_mtu - hdr_len)) + { + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) GKI_getbuf(buf_size)) == NULL) + { + /* whoops; free original msg buf and bail */ + AVCT_TRACE_ERROR0 ("avct_lcb_send_msg cannot alloc buffer!!"); + GKI_freebuf(p_data->ul_msg.p_buf); + break; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_lcb->peer_mtu - hdr_len; + + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_data->ul_msg.p_buf + 1) + p_data->ul_msg.p_buf->offset, p_buf->len); + + p_data->ul_msg.p_buf->offset += p_buf->len; + p_data->ul_msg.p_buf->len -= p_buf->len; + } + else + { + p_buf = p_data->ul_msg.p_buf; + } + + curr_msg_len -= p_buf->len; + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr); + if (pkt_type == AVCT_PKT_TYPE_START) + { + UINT8_TO_STREAM(p, nosp); + } + if ((pkt_type == AVCT_PKT_TYPE_START) || (pkt_type == AVCT_PKT_TYPE_SINGLE)) + { + UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid); + } + + if (p_lcb->cong == TRUE) + { + GKI_enqueue (&p_lcb->tx_q, p_buf); + } + + /* send message to L2CAP */ + else + { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_lcb->cong = TRUE; + } + } + + /* update pkt type for next packet */ + if (curr_msg_len > (p_lcb->peer_mtu - AVCT_HDR_LEN_END)) + { + pkt_type = AVCT_PKT_TYPE_CONT; + } + else + { + pkt_type = AVCT_PKT_TYPE_END; + } + } + AVCT_TRACE_DEBUG1 ("avct_lcb_send_msg tx_q_count:%d", p_lcb->tx_q.count); + return; +} + +/******************************************************************************* +** +** Function avct_lcb_free_msg_ind +** +** Description Discard an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + if (p_data) + GKI_freebuf(p_data->p_buf); + return; +} + +/******************************************************************************* +** +** Function avct_lcb_msg_ind +** +** Description Handle an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT8 *p; + UINT8 label, type, cr_ipid; + UINT16 pid; + tAVCT_CCB *p_ccb; + BT_HDR *p_buf; + + /* this p_buf is to be reported through p_msg_cback. The layer_specific + * needs to be set properly to indicate that it is received through + * control channel */ + p_data->p_buf->layer_specific = AVCT_DATA_CTRL; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_data->p_buf = avct_lcb_msg_asmbl(p_lcb, p_data->p_buf)) == NULL) + { + return; + } + + p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset; + + /* parse header byte */ + AVCT_PRS_HDR(p, label, type, cr_ipid); + + /* check for invalid cr_ipid */ + if (cr_ipid == AVCT_CR_IPID_INVALID) + { + AVCT_TRACE_WARNING1("Invalid cr_ipid", cr_ipid); + GKI_freebuf(p_data->p_buf); + return; + } + + /* parse and lookup PID */ + BE_STREAM_TO_UINT16(pid, p); + if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL) + { + /* PID found; send msg up, adjust bt hdr and call msg callback */ + p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE; + p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE; + (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf); + } + else + { + /* PID not found; drop message */ + AVCT_TRACE_WARNING1("No ccb for PID=%x", pid); + GKI_freebuf(p_data->p_buf); + + /* if command send reject */ + if (cr_ipid == AVCT_CMD) + { + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVCT_CMD_POOL_ID)) != NULL) + { + p_buf->len = AVCT_HDR_LEN_SINGLE; + p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ); + UINT16_TO_BE_STREAM(p, pid); + L2CA_DataWrite(p_lcb->ch_lcid, p_buf); + } + } + } +} diff --git a/stack/avdt/avdt_ad.c b/stack/avdt/avdt_ad.c new file mode 100644 index 0000000..92f429a --- /dev/null +++ b/stack/avdt/avdt_ad.c @@ -0,0 +1,628 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains the AVDTP adaption layer. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "wcassert.h" + + +/******************************************************************************* +** +** Function avdt_ad_type_to_tcid +** +** Description Derives the TCID from the channel type and SCB. +** +** +** Returns TCID value. +** +*******************************************************************************/ +UINT8 avdt_ad_type_to_tcid(UINT8 type, tAVDT_SCB *p_scb) +{ + UINT8 scb_idx; + + if (type == AVDT_CHAN_SIG) + { + return 0; + } + else + { + scb_idx = avdt_scb_to_hdl(p_scb) - 1; + /* + AVDT_TRACE_DEBUG2("type: %d, tcid: %d", type, ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type)); + */ + return ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type); + } +} + +/******************************************************************************* +** +** Function avdt_ad_tcid_to_type +** +** Description Derives the channel type from the TCID. +** +** +** Returns Channel type value. +** +*******************************************************************************/ +static UINT8 avdt_ad_tcid_to_type(UINT8 tcid) +{ + UINT8 type; + + if (tcid == 0) + { + type = AVDT_CHAN_SIG; + } + else + { + /* tcid translates to type based on number of channels, as follows: + ** only media channel : tcid=1,2,3,4,5,6... type=1,1,1,1,1,1... + ** media and report : tcid=1,2,3,4,5,6... type=1,2,1,2,1,2... + ** media, report, recov : tcid=1,2,3,4,5,6... type=1,2,3,1,2,3... + */ + type = ((tcid + AVDT_CHAN_NUM_TYPES - 2) % (AVDT_CHAN_NUM_TYPES - 1)) + 1; + } + AVDT_TRACE_DEBUG2("tcid: %d, type: %d", tcid, type); + return type; +} + + +/******************************************************************************* +** +** Function avdt_ad_init +** +** Description Initialize adaption layer. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_init(void) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + memset(&avdt_cb.ad, 0, sizeof(tAVDT_AD)); + + /* make sure the peer_mtu is a valid value */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) + { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_st +** +** Description Find adaption layer transport channel table entry matching +** the given state. +** +** +** Returns Pointer to matching entry. For control channel it returns +** the matching entry. For media or other it returns the +** first matching entry (there could be more than one). +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_st(UINT8 type, tAVDT_CCB *p_ccb, UINT8 state) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + UINT8 ccb_idx; + + if (p_ccb == NULL) + { + /* resending security req */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) + { + /* must be AVDT_CHAN_SIG - tcid always zero */ + if ((p_tbl->tcid == 0) && + (p_tbl->state == state)) + { + break; + } + } + } + else + { + ccb_idx = avdt_ccb_to_idx(p_ccb); + + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) + { + if (type == AVDT_CHAN_SIG) + { + /* if control channel, tcid always zero */ + if ((p_tbl->tcid == 0) && + (p_tbl->ccb_idx == ccb_idx) && + (p_tbl->state == state)) + { + break; + } + } + else + { + /* if other channel, tcid is always > zero */ + if ((p_tbl->tcid > 0) && + (p_tbl->ccb_idx == ccb_idx) && + (p_tbl->state == state)) + { + break; + } + } + } + } + + /* if nothing found return null */ + if (i == AVDT_NUM_TC_TBL) + { + p_tbl = NULL; + } + + return p_tbl; +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_lcid +** +** Description Find adaption layer transport channel table entry by LCID. +** +** +** Returns Pointer to entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + idx = avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < AVDT_NUM_TC_TBL) + { + return &avdt_cb.ad.tc_tbl[idx]; + } + else + { + return NULL; + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_by_type +** +** Description This function retrieves the transport channel table entry +** for a particular channel. +** +** +** Returns Pointer to transport channel table entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_by_type(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) +{ + UINT8 tcid; + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + UINT8 ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) + { + if ((p_tbl->tcid == tcid) && (p_tbl->ccb_idx == ccb_idx)) + { + break; + } + } + + WC_ASSERT(i != AVDT_NUM_TC_TBL); + + return p_tbl; +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_alloc +** +** Description Allocate an entry in the traffic channel table. +** +** +** Returns Pointer to entry. +** +*******************************************************************************/ +tAVDT_TC_TBL *avdt_ad_tc_tbl_alloc(tAVDT_CCB *p_ccb) +{ + int i; + tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl; + + /* find next free entry in tc table */ + for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) + { + if (p_tbl->state == AVDT_AD_ST_UNUSED) + { + break; + } + } + + /* sanity check */ + WC_ASSERT(i != AVDT_NUM_TC_TBL); + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags = 0; + p_tbl->ccb_idx = avdt_ccb_to_idx(p_ccb); + p_tbl->state = AVDT_AD_ST_IDLE; + + return p_tbl; +} + +/******************************************************************************* +** +** Function avdt_ad_tc_tbl_to_idx +** +** Description Convert a transport channel table entry to an index. +** +** +** Returns Index value. +** +*******************************************************************************/ +UINT8 avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL *p_tbl) +{ + AVDT_TRACE_DEBUG1("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdt_cb.ad.tc_tbl)); + /* use array arithmetic to determine index */ + return (UINT8) (p_tbl - avdt_cb.ad.tc_tbl); +} + +/******************************************************************************* +** +** Function avdt_ad_tc_close_ind +** +** Description This function is called by the L2CAP interface when the +** L2CAP channel is closed. It looks up the CCB or SCB for +** the channel and sends it a close event. The reason +** parameter is the same value passed by the L2CAP +** callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_close_ind(tAVDT_TC_TBL *p_tbl, UINT16 reason) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + tAVDT_SCB_TC_CLOSE close; + + close.old_tc_state = p_tbl->state; + /* clear avdt_ad_tc_tbl entry */ + p_tbl->state = AVDT_AD_ST_UNUSED; + p_tbl->cfg_flags = 0; + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + + AVDT_TRACE_DEBUG2("avdt_ad_tc_close_ind tcid: %d, old: %d", + p_tbl->tcid, close.old_tc_state); + /* if signaling channel, notify ccb that channel open */ + if (p_tbl->tcid == 0) + { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL); + } + /* if media or other channel, notify scb that channel close */ + else + { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) + { + close.tcid = p_tbl->tcid; + close.type = avdt_ad_tcid_to_type(p_tbl->tcid); + avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, (tAVDT_SCB_EVT *)&close); + } + } +} + +/******************************************************************************* +** +** Function avdt_ad_tc_open_ind +** +** Description This function is called by the L2CAP interface when +** the L2CAP channel is opened. It looks up the CCB or SCB +** for the channel and sends it an open event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + tAVDT_OPEN open; + tAVDT_EVT_HDR evt; + + p_tbl->state = AVDT_AD_ST_OPEN; + + /* if signaling channel, notify ccb that channel open */ + if (p_tbl->tcid == 0) + { + /* set the signal channel to use high priority within the ACL link */ + L2CA_SetTxPriority(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid, L2CAP_CHNL_PRIORITY_HIGH); + + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + /* use err_param to indicate the role of connection. + * AVDT_ACP, if ACP */ + evt.err_param = AVDT_INT; + if(p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) + { + evt.err_param = AVDT_ACP; + } + avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&evt); + } + /* if media or other channel, notify scb that channel open */ + else + { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + + /* put lcid in event data */ + if (p_scb != NULL) + { + open.peer_mtu = p_tbl->peer_mtu; + open.lcid = avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid; + open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid); + avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open); + } + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_cong_ind +** +** Description This function is called by the L2CAP interface layer when +** L2CAP calls the congestion callback. It looks up the CCB +** or SCB for the channel and sends it a congestion event. +** The is_congested parameter is the same value passed by +** the L2CAP callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_cong_ind(tAVDT_TC_TBL *p_tbl, BOOLEAN is_congested) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + + /* if signaling channel, notify ccb of congestion */ + if (p_tbl->tcid == 0) + { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, (tAVDT_CCB_EVT *) &is_congested); + } + /* if media or other channel, notify scb that channel open */ + else + { + /* look up scb in stream routing table by ccb, tcid */ + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, (tAVDT_SCB_EVT *) &is_congested); + } + } +} + + +/******************************************************************************* +** +** Function avdt_ad_tc_data_ind +** +** Description This function is called by the L2CAP interface layer when +** incoming data is received from L2CAP. It looks up the CCB +** or SCB for the channel and routes the data accordingly. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf) +{ + tAVDT_CCB *p_ccb; + tAVDT_SCB *p_scb; + + /* store type (media, recovery, reporting) */ + p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid); + + + /* if signaling channel, handle control message */ + if (p_tbl->tcid == 0) + { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + avdt_msg_ind(p_ccb, p_buf); + } + /* if media or other channel, send event to scb */ + else + { + p_scb = avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); + if (p_scb != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT *) &p_buf); + } + else + GKI_freebuf(p_buf); + } +} + +/******************************************************************************* +** +** Function avdt_ad_write_req +** +** Description This function is called by a CCB or SCB to send data to a +** transport channel. It looks up the LCID of the channel +** based on the type, CCB, and SCB (if present). Then it +** passes the data to L2CA_DataWrite(). +** +** +** Returns AVDT_AD_SUCCESS, if data accepted, else FALSE +** AVDT_AD_CONGESTED, if data accepted and the channel is congested +** AVDT_AD_FAILED, if error +** +*******************************************************************************/ +UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf) +{ + UINT8 tcid; + + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + + return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf); +} + + +/******************************************************************************* +** +** Function avdt_ad_open_req +** +** Description This function is called by a CCB or SCB to open a transport +** channel. This function allocates and initializes a +** transport channel table entry. The channel can be opened +** in two roles: as an initiator or acceptor. When opened +** as an initiator the function will start an L2CAP connection. +** When opened as an acceptor the function simply configures +** the table entry to listen for an incoming channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role) +{ + tAVDT_TC_TBL *p_tbl; + UINT16 lcid; + + p_tbl = avdt_ad_tc_tbl_alloc(p_ccb); + + p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb); + AVDT_TRACE_DEBUG3("avdt_ad_open_req: type: %d, role: %d, tcid:%d", + type, role, p_tbl->tcid); + + if (type == AVDT_CHAN_SIG) + { + /* if signaling, get mtu from registration control block */ + p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; + p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; + } + else + { + /* otherwise get mtu from scb */ + p_tbl->my_mtu = p_scb->cs.mtu; + p_tbl->my_flush_to = p_scb->cs.flush_to; + + /* also set scb_hdl in rt_tbl */ + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl = avdt_scb_to_hdl(p_scb); + AVDT_TRACE_DEBUG3("avdt_cb.ad.rt_tbl[%d][%d].scb_hdl = %d", + avdt_ccb_to_idx(p_ccb), p_tbl->tcid, + avdt_scb_to_hdl(p_scb)); + } + + /* if we're acceptor, we're done; just sit back and listen */ + if (role == AVDT_ACP) + { + p_tbl->state = AVDT_AD_ST_ACP; + } + /* else we're inititator, start the L2CAP connection */ + else + { + p_tbl->state = AVDT_AD_ST_CONN; + + /* call l2cap connect req */ + if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 0) + { + /* if connect req ok, store tcid in lcid table */ + avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + AVDT_TRACE_DEBUG2("avdt_cb.ad.lcid_tbl[%d] = %d", + (lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); + + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; + AVDT_TRACE_DEBUG3("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x", + avdt_ccb_to_idx(p_ccb), p_tbl->tcid, + lcid); + } + else + { + /* if connect req failed, call avdt_ad_tc_close_ind() */ + avdt_ad_tc_close_ind(p_tbl, 0); + } + } +} + +/******************************************************************************* +** +** Function avdt_ad_close_req +** +** Description This function is called by a CCB or SCB to close a +** transport channel. The function looks up the LCID for the +** channel and calls L2CA_DisconnectReq(). +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ad_close_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb) +{ + UINT8 tcid; + tAVDT_TC_TBL *p_tbl; + + p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb); + AVDT_TRACE_DEBUG1("avdt_ad_close_req state: %d", p_tbl->state); + + switch(p_tbl->state) + { + case AVDT_AD_ST_UNUSED: + /* probably for reporting */ + break; + case AVDT_AD_ST_ACP: + /* if we're listening on this channel, send ourselves a close ind */ + avdt_ad_tc_close_ind(p_tbl, 0); + break; + default: + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(type, p_scb); + + /* call l2cap disconnect req */ + L2CA_DisconnectReq(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid); + } +} + diff --git a/stack/avdt/avdt_api.c b/stack/avdt/avdt_api.c new file mode 100644 index 0000000..67fe452 --- /dev/null +++ b/stack/avdt/avdt_api.c @@ -0,0 +1,1383 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains API of the audio/video distribution transport + * protocol. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "l2c_api.h" +#include "btm_api.h" +#include "btu.h" + + +#if (defined BTTRC_INCLUDED && BTTRC_INCLUDED == TRUE) +#include "bttrc_str_ids.h" +#endif + +/* Control block for AVDT */ +#if AVDT_DYNAMIC_MEMORY == FALSE +tAVDT_CB avdt_cb; +#endif + + +/******************************************************************************* +** +** Function avdt_process_timeout +** +** Description This function is called by BTU when an AVDTP timer +** expires. The function sends a timer event to the +** appropriate CCB or SCB state machine. +** +** This function is for use internal to the stack only. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_process_timeout(TIMER_LIST_ENT *p_tle) +{ + UINT8 event = 0; + UINT8 err_code = AVDT_ERR_TIMEOUT; + + switch (p_tle->event) + { + case BTU_TTYPE_AVDT_CCB_RET: + event = AVDT_CCB_RET_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_CCB_RSP: + event = AVDT_CCB_RSP_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_CCB_IDLE: + event = AVDT_CCB_IDLE_TOUT_EVT + AVDT_CCB_MKR; + break; + + case BTU_TTYPE_AVDT_SCB_TC: + event = AVDT_SCB_TC_TOUT_EVT; + break; + + default: + break; + } + + if (event & AVDT_CCB_MKR) + { + avdt_ccb_event((tAVDT_CCB *) p_tle->param, (UINT8) (event & ~AVDT_CCB_MKR), + (tAVDT_CCB_EVT *) &err_code); + } + else + { + avdt_scb_event((tAVDT_SCB *) p_tle->param, event, NULL); + } +} + +/******************************************************************************* +** +** Function AVDT_Register +** +** Description This is the system level registration function for the +** AVDTP protocol. This function initializes AVDTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVDTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback) +{ + + BTTRC_AVDT_API0(AVDT_TRACE_API_REGISTER); + + /* register PSM with L2CAP */ + L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO *) &avdt_l2c_appl); + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); + + /* do not use security on the media channel */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA); + +#if AVDT_REPORTING == TRUE + /* do not use security on the reporting channel */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT); +#endif + + /* initialize AVDTP data structures */ + avdt_scb_init(); + avdt_ccb_init(); + avdt_ad_init(); +#if defined(AVDT_INITIAL_TRACE_LEVEL) + avdt_cb.trace_level = AVDT_INITIAL_TRACE_LEVEL; +#else + avdt_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + + /* copy registration struct */ + memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG)); + avdt_cb.p_conn_cback = p_cback; +} + +/******************************************************************************* +** +** Function AVDT_Deregister +** +** Description This function is called to deregister use AVDTP protocol. +** It is called when AVDTP is no longer being used by any +** application in the system. Before this function can be +** called, all streams must be removed with AVDT_RemoveStream(). +** +** +** Returns void +** +*******************************************************************************/ +void AVDT_Deregister(void) +{ + BTTRC_AVDT_API0(AVDT_TRACE_API_DEREGISTER); + + /* deregister PSM with L2CAP */ + L2CA_Deregister(AVDT_PSM); +} + +/******************************************************************************* +** +** Function AVDT_CreateStream +** +** Description Create a stream endpoint. After a stream endpoint is +** created an application can initiate a connection between +** this endpoint and an endpoint on a peer device. In +** addition, a peer device can discover, get the capabilities, +** and connect to this endpoint. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs) +{ + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB *p_scb; + + BTTRC_AVDT_API0(AVDT_TRACE_API_CREATESTREAM); + + /* Verify parameters; if invalid, return failure */ + if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) || (p_cs->p_ctrl_cback == NULL)) + { + result = AVDT_BAD_PARAMS; + } + /* Allocate scb; if no scbs, return failure */ + else if ((p_scb = avdt_scb_alloc(p_cs)) == NULL) + { + result = AVDT_NO_RESOURCES; + } + else + { + *p_handle = avdt_scb_to_hdl(p_scb); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_RemoveStream +** +** Description Remove a stream endpoint. This function is called when +** the application is no longer using a stream endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and then the stream endpoint +** is removed. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_RemoveStream(UINT8 handle) +{ + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB *p_scb; + + + BTTRC_AVDT_API0(AVDT_TRACE_API_REMOVESTREAM); + + /* look up scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + { + /* send remove event to scb */ + avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_DiscoverReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and discovers +** the stream endpoints on the peer device. (Please note +** that AVDTP discovery is unrelated to SDP discovery). +** This function can be called at any time regardless of whether +** there is an AVDTP connection to the peer device. +** +** When discovery is complete, an AVDT_DISCOVER_CFM_EVT +** is sent to the application via its callback function. +** The application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again to the same device until +** discovery is complete. +** +** The memory addressed by sep_info is allocated by the +** application. This memory is written to by AVDTP as part +** of the discovery procedure. This memory must remain +** accessible until the application receives the +** AVDT_DISCOVER_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, + UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_DISCOVER_REQ); + + /* find channel control block for this bd addr; if none, allocate one */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + if (result == AVDT_SUCCESS) + { + /* make sure no discovery or get capabilities req already in progress */ + if (p_ccb->proc_busy) + { + result = AVDT_BUSY; + } + /* send event to ccb */ + else + { + evt.discover.p_sep_info = p_sep_info; + evt.discover.num_seps = max_seps; + evt.discover.p_cback = p_cback; + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function avdt_get_cap_req +** +** Description internal function to serve both AVDT_GetCapReq and +** AVDT_GetAllCapReq +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +static UINT16 avdt_get_cap_req(BD_ADDR bd_addr, tAVDT_CCB_API_GETCAP *p_evt) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + + /* verify SEID */ + if ((p_evt->single.seid < AVDT_SEID_MIN) || (p_evt->single.seid > AVDT_SEID_MAX)) + { + AVDT_TRACE_ERROR1("seid: %d", p_evt->single.seid); + result = AVDT_BAD_PARAMS; + } + /* find channel control block for this bd addr; if none, allocate one */ + else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + if (result == AVDT_SUCCESS) + { + /* make sure no discovery or get capabilities req already in progress */ + if (p_ccb->proc_busy) + { + result = AVDT_BUSY; + } + /* send event to ccb */ + else + { + avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT *)p_evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_GetCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_GetCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB_API_GETCAP getcap; + + BTTRC_AVDT_API1(AVDT_TRACE_API_GETCAP_REQ, BTTRC_PARAM_UINT8, seid); + + getcap.single.seid = seid; + getcap.single.sig_id = AVDT_SIG_GETCAP; + getcap.p_cfg = p_cfg; + getcap.p_cback = p_cback; + return avdt_get_cap_req (bd_addr, &getcap); +} + +/******************************************************************************* +** +** Function AVDT_GetAllCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB_API_GETCAP getcap; + + BTTRC_AVDT_API1(AVDT_TRACE_API_GET_ALLCAP_REQ, BTTRC_PARAM_UINT8, seid); + + getcap.single.seid = seid; + getcap.single.sig_id = AVDT_SIG_GET_ALLCAP; + getcap.p_cfg = p_cfg; + getcap.p_cback = p_cback; + return avdt_get_cap_req (bd_addr, &getcap); +} + +/******************************************************************************* +** +** Function AVDT_DelayReport +** +** Description This functions sends a Delay Report to the peer device +** that is associated with a particular SEID. +** This function is called by SNK device. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_DELAY_REPORT); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + /* send event to scb */ + { + evt.apidelay.hdr.seid = seid; + evt.apidelay.delay = delay; + avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_OpenReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and connects +** to a stream endpoint on a peer device. When the connection +** is completed, an AVDT_OPEN_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg) +{ + tAVDT_CCB *p_ccb = NULL; + tAVDT_SCB *p_scb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_OPEN_REQ); + + + /* verify SEID */ + if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) + { + result = AVDT_BAD_PARAMS; + } + /* map handle to scb */ + else if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* find channel control block for this bd addr; if none, allocate one */ + else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + + /* send event to scb */ + if (result == AVDT_SUCCESS) + { + evt.msg.config_cmd.hdr.seid = seid; + evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + evt.msg.config_cmd.int_seid = handle; + evt.msg.config_cmd.p_cfg = p_cfg; + avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_ConfigRsp +** +** Description Respond to a configure request from the peer device. This +** function must be called if the application receives an +** AVDT_CONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + UINT8 event_code; + + BTTRC_AVDT_API0(AVDT_TRACE_API_CONFIG_RSP); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* handle special case when this function is called but peer has not send + ** a configuration cmd; ignore and return error result + */ + else if (!p_scb->in_use) + { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else + { + evt.msg.hdr.err_code = error_code; + evt.msg.hdr.err_param = category; + evt.msg.hdr.label = label; + if (error_code == 0) + { + event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT; + } + else + { + event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT; + } + avdt_scb_event(p_scb, event_code, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_StartReq +** +** Description Start one or more stream endpoints. This initiates the +** transfer of media packets for the streams. All stream +** endpoints must previously be opened. When the streams +** are started, an AVDT_START_CFM_EVT is sent to the +** application via the control callback function for each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_StartReq(UINT8 *p_handles, UINT8 num_handles) +{ + tAVDT_SCB *p_scb = NULL; + tAVDT_CCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + int i; + + BTTRC_AVDT_API0(AVDT_TRACE_API_START_REQ); + + if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) + { + result = AVDT_BAD_PARAMS; + } + else + { + /* verify handles */ + for (i = 0; i < num_handles; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL) + { + result = AVDT_BAD_HANDLE; + break; + } + } + } + + if (result == AVDT_SUCCESS) + { + if (p_scb->p_ccb == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + { + /* send event to ccb */ + memcpy(evt.msg.multi.seid_list, p_handles, num_handles); + evt.msg.multi.num_seps = num_handles; + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_START_REQ_EVT, &evt); + } + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_SuspendReq +** +** Description Suspend one or more stream endpoints. This suspends the +** transfer of media packets for the streams. All stream +** endpoints must previously be open and started. When the +** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to +** the application via the control callback function for +** each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles) +{ + tAVDT_SCB *p_scb = NULL; + tAVDT_CCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + int i; + + BTTRC_AVDT_API0(AVDT_TRACE_API_SUSPEND_REQ); + + if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) + { + result = AVDT_BAD_PARAMS; + } + else + { + /* verify handles */ + for (i = 0; i < num_handles; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL) + { + result = AVDT_BAD_HANDLE; + break; + } + } + } + + if (result == AVDT_SUCCESS) + { + if (p_scb->p_ccb == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + { + /* send event to ccb */ + memcpy(evt.msg.multi.seid_list, p_handles, num_handles); + evt.msg.multi.num_seps = num_handles; + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt); + } + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_CloseReq +** +** Description Close a stream endpoint. This stops the transfer of media +** packets and closes the transport channel associated with +** this stream endpoint. When the stream is closed, an +** AVDT_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_CloseReq(UINT8 handle) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + + BTTRC_AVDT_API0(AVDT_TRACE_API_CLOSE_REQ); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + /* send event to scb */ + { + avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_ReconfigReq +** +** Description Reconfigure a stream endpoint. This allows the application +** to change the codec or content protection capabilities of +** a stream endpoint after it has been opened. This function +** can only be called if the stream is opened but not started +** or if the stream has been suspended. When the procedure +** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_RECONFIG_REQ); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else + { + /* force psc_mask to zero */ + p_cfg->psc_mask = 0; + + evt.msg.reconfig_cmd.p_cfg = p_cfg; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_ReconfigRsp +** +** Description Respond to a reconfigure request from the peer device. +** This function must be called if the application receives +** an AVDT_RECONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + + BTTRC_AVDT_API0(AVDT_TRACE_API_RECONFIG_RSP); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else + { + evt.msg.hdr.err_code = error_code; + evt.msg.hdr.err_param = category; + evt.msg.hdr.label = label; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_SecurityReq +** +** Description Send a security request to the peer device. When the +** security procedure is completed, an AVDT_SECURITY_CFM_EVT +** is sent to the application via the control callback function +** for this handle. (Please note that AVDTP security procedures +** are unrelated to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_SECURITY_REQ); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else + { + evt.msg.security_rsp.p_data = p_data; + evt.msg.security_rsp.len = len; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_SecurityRsp +** +** Description Respond to a security request from the peer device. +** This function must be called if the application receives +** an AVDT_SECURITY_IND_EVT through its control callback. +** (Please note that AVDTP security procedures are unrelated +** to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 *p_data, UINT16 len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + tAVDT_SCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_SECURITY_RSP); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + /* send event to scb */ + else + { + evt.msg.security_rsp.hdr.err_code = error_code; + evt.msg.security_rsp.hdr.label = label; + evt.msg.security_rsp.p_data = p_data; + evt.msg.security_rsp.len = len; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_WriteReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure. +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET. +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_WriteReq(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt) +{ + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + + BTTRC_AVDT_API0(AVDT_TRACE_API_WRITE_REQ); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + { + evt.apiwrite.p_buf = p_pkt; + evt.apiwrite.time_stamp = time_stamp; + evt.apiwrite.m_pt = m_pt; +#if AVDT_MULTIPLEXING == TRUE + GKI_init_q (&evt.apiwrite.frag_q); +#endif + avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); + } + + return result; +} + +/******************************************************************************* +** +** Function AVDT_ConnectReq +** +** Description This function initiates an AVDTP signaling connection +** to the peer device. When the connection is completed, an +** AVDT_CONNECT_IND_EVT is sent to the application via its +** control callback function. If the connection attempt fails +** an AVDT_DISCONNECT_IND_EVT is sent. The security mask +** parameter overrides the outgoing security mask set in +** AVDT_Register(). +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_CONNECT_REQ); + + /* find channel control block for this bd addr; if none, allocate one */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* could not allocate channel control block */ + result = AVDT_NO_RESOURCES; + } + } + else if (p_ccb->ll_opened == FALSE) + { + AVDT_TRACE_WARNING0("AVDT_ConnectReq: CCB LL is in the middle of opening"); + + /* ccb was already allocated for the incoming signalling. */ + result = AVDT_BUSY; + } + + if (result == AVDT_SUCCESS) + { + /* send event to ccb */ + evt.connect.p_cback = p_cback; + evt.connect.sec_mask = sec_mask; + avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_DisconnectReq +** +** Description This function disconnect an AVDTP signaling connection +** to the peer device. When disconnected an +** AVDT_DISCONNECT_IND_EVT is sent to the application via its +** control callback function. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback) +{ + tAVDT_CCB *p_ccb = NULL; + UINT16 result = AVDT_SUCCESS; + tAVDT_CCB_EVT evt; + + BTTRC_AVDT_API0(AVDT_TRACE_API_DISCONNECT_REQ); + + + /* find channel control block for this bd addr; if none, error */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + result = AVDT_BAD_PARAMS; + } + + if (result == AVDT_SUCCESS) + { + /* send event to ccb */ + evt.disconnect.p_cback = p_cback; + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt); + } + return result; +} + +/******************************************************************************* +** +** Function AVDT_GetL2CapChannel +** +** Description Get the L2CAP CID used by the handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +UINT16 AVDT_GetL2CapChannel(UINT8 handle) +{ + tAVDT_SCB *p_scb; + tAVDT_CCB *p_ccb; + UINT8 tcid; + UINT16 lcid = 0; + + /* map handle to scb */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && ((p_ccb = p_scb->p_ccb) != NULL)) + { + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } + + BTTRC_AVDT_API1(AVDT_TRACE_API_GET_L2CAP_CHAN, BTTRC_PARAM_UINT16, lcid); + + return (lcid); +} + +/******************************************************************************* +** +** Function AVDT_GetSignalChannel +** +** Description Get the L2CAP CID used by the signal channel of the given handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr) +{ + tAVDT_SCB *p_scb; + tAVDT_CCB *p_ccb; + UINT8 tcid = 0; /* tcid is always 0 for signal channel */ + UINT16 lcid = 0; + + /* map handle to scb */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && ((p_ccb = p_scb->p_ccb) != NULL)) + { + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } + else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) != NULL) + { + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + } + + BTTRC_AVDT_API1(AVDT_TRACE_API_GET_SIGNAL_CHAN, BTTRC_PARAM_UINT16, lcid); + + return (lcid); +} + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function AVDT_WriteDataReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteDataReq(). If the applications calls +** AVDT_WriteDataReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteDataReq() after it receives an +** AVDT_START_CFM_EVT or AVDT_START_IND_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_WriteDataReq(UINT8 handle, UINT8 *p_data, UINT32 data_len, + UINT32 time_stamp, UINT8 m_pt, UINT8 marker) +{ + + tAVDT_SCB *p_scb; + tAVDT_SCB_EVT evt; + UINT16 result = AVDT_SUCCESS; + + BTTRC_AVDT_API1(AVDT_TRACE_API_WRITE_DATA_REQ, BTTRC_PARAM_UINT32, data_len); + + do + { + /* check length of media frame */ + if(data_len > AVDT_MAX_MEDIA_SIZE) + { + result = AVDT_BAD_PARAMS; + break; + } + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + break; + } + AVDT_TRACE_WARNING1("mux_tsid_media:%d", p_scb->curr_cfg.mux_tsid_media); + + if (p_scb->p_pkt != NULL + || p_scb->p_ccb == NULL + || !GKI_queue_is_empty(&p_scb->frag_q) + || p_scb->frag_off != 0 + || p_scb->curr_cfg.mux_tsid_media == 0) + { + result = AVDT_ERR_BAD_STATE; + AVDT_TRACE_WARNING4("p_scb->p_pkt=%x, p_scb->p_ccb=%x, IsQueueEmpty=%x, p_scb->frag_off=%x", + p_scb->p_pkt, p_scb->p_ccb, GKI_queue_is_empty(&p_scb->frag_q), p_scb->frag_off); + break; + } + evt.apiwrite.p_buf = 0; /* it will indicate using of fragments queue frag_q */ + /* create queue of media fragments */ + GKI_init_q (&evt.apiwrite.frag_q); + + /* compose fragments from media payload and put fragments into gueue */ + avdt_scb_queue_frags(p_scb, &p_data, &data_len, &evt.apiwrite.frag_q); + + if(GKI_queue_is_empty(&evt.apiwrite.frag_q)) + { + AVDT_TRACE_WARNING0("AVDT_WriteDataReq out of GKI buffers"); + result = AVDT_ERR_RESOURCE; + break; + } + evt.apiwrite.data_len = data_len; + evt.apiwrite.p_data = p_data; + + /* process the fragments queue */ + evt.apiwrite.time_stamp = time_stamp; + evt.apiwrite.m_pt = m_pt | (marker<<7); + avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt); + } while (0); + +#if (BT_USE_TRACES == TRUE) + if(result != AVDT_SUCCESS) + { + AVDT_TRACE_WARNING1("*** AVDT_WriteDataReq failed result=%d",result); + } +#endif + return result; +} +#endif + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function AVDT_SetMediaBuf +** +** Description Assigns buffer for media packets or forbids using of assigned +** buffer if argument p_buf is NULL. This function can only +** be called if the stream is a SNK. +** +** AVDTP uses this buffer to reassemble fragmented media packets. +** When AVDTP receives a complete media packet, it calls the +** p_media_cback assigned by AVDT_CreateStream(). +** This function can be called during callback to assign a +** different buffer for next media packet or can leave the current +** buffer for next packet. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_SUCCESS; + + BTTRC_AVDT_API0(AVDT_TRACE_API_SET_MEDIABUF); + + /* map handle to scb */ + if ((p_scb = avdt_scb_by_hdl(handle)) == NULL) + { + result = AVDT_BAD_HANDLE; + } + else + { + if(p_buf && p_scb->cs.p_media_cback == NULL) + result = AVDT_NO_RESOURCES; + else + { + p_scb->p_media_buf = p_buf; + p_scb->media_buf_len = buf_len; + } + } + + return result; +} +#endif + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function AVDT_SendReport +** +** Description +** +** +** +** Returns +** +*******************************************************************************/ +UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data) +{ + tAVDT_SCB *p_scb; + UINT16 result = AVDT_BAD_PARAMS; + BT_HDR *p_pkt; + tAVDT_TC_TBL *p_tbl; + UINT8 *p, *plen, *pm1, *p_end; +#if AVDT_MULTIPLEXING == TRUE + UINT8 *p_al, u; +#endif + UINT32 ssrc; + UINT16 len; + + BTTRC_AVDT_API0(AVDT_TRACE_API_SEND_REPORT); + + /* map handle to scb && verify parameters */ + if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) + && (p_scb->p_ccb != NULL) + && (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) || + ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) || + (type == AVDT_RTCP_PT_SDES)) ) + { + result = AVDT_NO_RESOURCES; + + /* build SR - assume fit in one packet */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); + if((p_tbl->state == AVDT_AD_ST_OPEN) && + (p_pkt = (BT_HDR *)GKI_getbuf(p_tbl->peer_mtu)) != NULL) + { + p_pkt->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; +#if AVDT_MULTIPLEXING == TRUE + if(p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) + { + /* Adaptation Layer header later */ + p_al = p; + p += 2; + } +#endif + pm1 = p; + *p++ = AVDT_MEDIA_OCTET1 | 1; + *p++ = type; + /* save the location for length */ + plen = p; + p+= 2; + ssrc = avdt_scb_gen_ssrc(p_scb); + UINT32_TO_BE_STREAM(p, ssrc); + + switch(type) + { + case AVDT_RTCP_PT_SR: /* Sender Report */ + *pm1 = AVDT_MEDIA_OCTET1; + UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec); + UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac); + UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time); + UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count); + UINT32_TO_BE_STREAM(p, p_data->sr.octet_count); + break; + + case AVDT_RTCP_PT_RR: /* Receiver Report */ + *p++ = p_data->rr.frag_lost; + AVDT_TRACE_API1("packet_lost: %d", p_data->rr.packet_lost); + p_data->rr.packet_lost &= 0xFFFFFF; + AVDT_TRACE_API1("packet_lost: %d", p_data->rr.packet_lost); + UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost); + UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd); + UINT32_TO_BE_STREAM(p, p_data->rr.jitter); + UINT32_TO_BE_STREAM(p, p_data->rr.lsr); + UINT32_TO_BE_STREAM(p, p_data->rr.dlsr); + break; + + case AVDT_RTCP_PT_SDES: /* Source Description */ + *p++ = AVDT_RTCP_SDES_CNAME; + len = strlen((char *)p_data->cname); + if(len > AVDT_MAX_CNAME_SIZE) + len = AVDT_MAX_CNAME_SIZE; + *p++ = (UINT8)len; + BCM_STRNCPY_S((char *)p, len+1, (char *)p_data->cname, len+1); + p += len; + break; + } + p_end = p; + len = p - pm1 - 1; + UINT16_TO_BE_STREAM(plen, len); + +#if AVDT_MULTIPLEXING == TRUE + if(p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX) + { + /* Adaptation Layer header */ + p = p_al; + len++; + UINT16_TO_BE_STREAM(p_al, len ); + /* TSID, no-fragment bit and coding of length(9-bit length field) */ + u = *p; + *p = (p_scb->curr_cfg.mux_tsid_report<<3) | AVDT_ALH_LCODE_9BITM0; + if(u) + *p |= AVDT_ALH_LCODE_9BITM1; + } +#endif + + /* set the actual payload length */ + p_pkt->len = p_end - p; + /* send the packet */ + if(L2CAP_DW_FAILED != avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt)) + result = AVDT_SUCCESS; + } + } + + return result; +} +#endif + +/****************************************************************************** +** +** Function AVDT_SetTraceLevel +** +** Description Sets the trace level for AVDT. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVDT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 AVDT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + avdt_cb.trace_level = new_level; + + return (avdt_cb.trace_level); +} + diff --git a/stack/avdt/avdt_ccb.c b/stack/avdt/avdt_ccb.c new file mode 100644 index 0000000..9cb40b2 --- /dev/null +++ b/stack/avdt/avdt_ccb.c @@ -0,0 +1,464 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains the channel control block state machine and + * functions which operate on the channel control block. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "gki.h" +#include "btu.h" + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ +#if AVDT_DEBUG == TRUE + +/* verbose state strings for trace */ +const char * const avdt_ccb_st_str[] = { + "CCB_IDLE_ST", + "CCB_OPENING_ST", + "CCB_OPEN_ST", + "CCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char * const avdt_ccb_evt_str[] = { + "API_DISCOVER_REQ_EVT", + "API_GETCAP_REQ_EVT", + "API_START_REQ_EVT", + "API_SUSPEND_REQ_EVT", + "API_DISCOVER_RSP_EVT", + "API_GETCAP_RSP_EVT", + "API_START_RSP_EVT", + "API_SUSPEND_RSP_EVT", + "API_CONNECT_REQ_EVT", + "API_DISCONNECT_REQ_EVT", + "MSG_DISCOVER_CMD_EVT", + "MSG_GETCAP_CMD_EVT", + "MSG_START_CMD_EVT", + "MSG_SUSPEND_CMD_EVT", + "MSG_DISCOVER_RSP_EVT", + "MSG_GETCAP_RSP_EVT", + "MSG_START_RSP_EVT", + "MSG_SUSPEND_RSP_EVT", + "RCVRSP_EVT", + "SENDMSG_EVT", + "RET_TOUT_EVT", + "RSP_TOUT_EVT", + "IDLE_TOUT_EVT", + "UL_OPEN_EVT", + "UL_CLOSE_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_CONG_EVT" +}; + +#endif + + +/* action function list */ +const tAVDT_CCB_ACTION avdt_ccb_action[] = { + avdt_ccb_chan_open, + avdt_ccb_chan_close, + avdt_ccb_chk_close, + avdt_ccb_hdl_discover_cmd, + avdt_ccb_hdl_discover_rsp, + avdt_ccb_hdl_getcap_cmd, + avdt_ccb_hdl_getcap_rsp, + avdt_ccb_hdl_start_cmd, + avdt_ccb_hdl_start_rsp, + avdt_ccb_hdl_suspend_cmd, + avdt_ccb_hdl_suspend_rsp, + avdt_ccb_snd_discover_cmd, + avdt_ccb_snd_discover_rsp, + avdt_ccb_snd_getcap_cmd, + avdt_ccb_snd_getcap_rsp, + avdt_ccb_snd_start_cmd, + avdt_ccb_snd_start_rsp, + avdt_ccb_snd_suspend_cmd, + avdt_ccb_snd_suspend_rsp, + avdt_ccb_clear_cmds, + avdt_ccb_cmd_fail, + avdt_ccb_free_cmd, + avdt_ccb_cong_state, + avdt_ccb_ret_cmd, + avdt_ccb_snd_cmd, + avdt_ccb_snd_msg, + avdt_ccb_set_reconn, + avdt_ccb_clr_reconn, + avdt_ccb_chk_reconn, + avdt_ccb_chk_timer, + avdt_ccb_set_conn, + avdt_ccb_set_disconn, + avdt_ccb_do_disconn, + avdt_ccb_ll_closed, + avdt_ccb_ll_opened, + avdt_ccb_dealloc +}; + +/* state table information */ +#define AVDT_CCB_ACTIONS 2 /* number of actions */ +#define AVDT_CCB_NEXT_STATE 2 /* position of next state */ +#define AVDT_CCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avdt_ccb_st_idle[][AVDT_CCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, +/* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, +/* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_CHAN_OPEN, AVDT_CCB_OPENING_ST}, +/* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* UL_OPEN_EVT */ {AVDT_CCB_CHAN_OPEN, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* UL_CLOSE_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* LL_OPEN_EVT */ {AVDT_CCB_LL_OPENED, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avdt_ccb_st_opening[][AVDT_CCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_SET_DISCONN, AVDT_CCB_DO_DISCONN, AVDT_CCB_CLOSING_ST}, +/* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* UL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST}, +/* UL_CLOSE_EVT */ {AVDT_CCB_CLEAR_CMDS, AVDT_CCB_CHAN_CLOSE, AVDT_CCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVDT_CCB_SND_CMD, AVDT_CCB_LL_OPENED, AVDT_CCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVDT_CCB_CONG_STATE, AVDT_CCB_IGNORE, AVDT_CCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avdt_ccb_st_open[][AVDT_CCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_GETCAP_REQ_EVT */ {AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_START_REQ_EVT */ {AVDT_CCB_SND_START_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_SUSPEND_REQ_EVT */ {AVDT_CCB_SND_SUSPEND_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_DISCOVER_RSP_EVT */ {AVDT_CCB_SND_DISCOVER_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_GETCAP_RSP_EVT */ {AVDT_CCB_SND_GETCAP_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_START_RSP_EVT */ {AVDT_CCB_SND_START_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_SUSPEND_RSP_EVT */ {AVDT_CCB_SND_SUSPEND_RSP, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_CONN, AVDT_CCB_LL_OPENED, AVDT_CCB_OPEN_ST}, +/* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_SET_DISCONN, AVDT_CCB_DO_DISCONN, AVDT_CCB_CLOSING_ST}, +/* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_HDL_DISCOVER_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_HDL_GETCAP_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* MSG_START_CMD_EVT */ {AVDT_CCB_HDL_START_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_HDL_SUSPEND_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_OPEN_ST}, +/* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_OPEN_ST}, +/* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* RCVRSP_EVT */ {AVDT_CCB_FREE_CMD, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* SENDMSG_EVT */ {AVDT_CCB_SND_MSG, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* RET_TOUT_EVT */ {AVDT_CCB_RET_CMD, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* RSP_TOUT_EVT */ {AVDT_CCB_CMD_FAIL, AVDT_CCB_SND_CMD, AVDT_CCB_OPEN_ST}, +/* IDLE_TOUT_EVT */ {AVDT_CCB_CLEAR_CMDS, AVDT_CCB_CHAN_CLOSE, AVDT_CCB_CLOSING_ST}, +/* UL_OPEN_EVT */ {AVDT_CCB_CHK_TIMER, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* UL_CLOSE_EVT */ {AVDT_CCB_CHK_CLOSE, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* LL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVDT_CCB_LL_CLOSED, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVDT_CCB_CONG_STATE, AVDT_CCB_SND_MSG, AVDT_CCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 avdt_ccb_st_closing[][AVDT_CCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_DISCOVER_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SND_DISCOVER_CMD, AVDT_CCB_CLOSING_ST}, +/* API_GETCAP_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SND_GETCAP_CMD, AVDT_CCB_CLOSING_ST}, +/* API_START_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_SUSPEND_REQ_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_DISCOVER_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_GETCAP_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_START_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_SUSPEND_RSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* API_CONNECT_REQ_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_SET_CONN, AVDT_CCB_CLOSING_ST}, +/* API_DISCONNECT_REQ_EVT */ {AVDT_CCB_CLR_RECONN, AVDT_CCB_SET_DISCONN, AVDT_CCB_CLOSING_ST}, +/* MSG_DISCOVER_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_GETCAP_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_START_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_DISCOVER_RSP_EVT */ {AVDT_CCB_HDL_DISCOVER_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_GETCAP_RSP_EVT */ {AVDT_CCB_HDL_GETCAP_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_START_RSP_EVT */ {AVDT_CCB_HDL_START_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_CCB_HDL_SUSPEND_RSP, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* RCVRSP_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* SENDMSG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* RET_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* RSP_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* IDLE_TOUT_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* UL_OPEN_EVT */ {AVDT_CCB_SET_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* UL_CLOSE_EVT */ {AVDT_CCB_CLR_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST}, +/* LL_CLOSE_EVT */ {AVDT_CCB_CHK_RECONN, AVDT_CCB_IGNORE, AVDT_CCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVDT_CCB_IGNORE, AVDT_CCB_IGNORE, AVDT_CCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVDT_CCB_ST_TBL)[AVDT_CCB_NUM_COLS]; + +/* state table */ +const tAVDT_CCB_ST_TBL avdt_ccb_st_tbl[] = { + avdt_ccb_st_idle, + avdt_ccb_st_opening, + avdt_ccb_st_open, + avdt_ccb_st_closing +}; + +/******************************************************************************* +** +** Function avdt_ccb_init +** +** Description Initialize channel control block module. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ccb_init(void) +{ + memset(&avdt_cb.ccb[0], 0, sizeof(tAVDT_CCB) * AVDT_NUM_LINKS); + avdt_cb.p_ccb_act = (tAVDT_CCB_ACTION *) avdt_ccb_action; +} + +/******************************************************************************* +** +** Function avdt_ccb_event +** +** Description State machine event handling function for ccb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CCB_ST_TBL state_table; + UINT8 action; + int i; + +#if AVDT_DEBUG == TRUE + AVDT_TRACE_EVENT3("CCB ccb=%d event=%s state=%s", avdt_ccb_to_idx(p_ccb), avdt_ccb_evt_str[event], avdt_ccb_st_str[p_ccb->state]); +#endif + BTTRC_AVDT_CCB_EVENT(event, p_ccb->state); + + /* look up the state table for the current state */ + state_table = avdt_ccb_st_tbl[p_ccb->state]; + + /* set next state */ + if (p_ccb->state != state_table[event][AVDT_CCB_NEXT_STATE]) + BTTRC_AVDT_CCB_STATE(state_table[event][AVDT_CCB_NEXT_STATE]); + p_ccb->state = state_table[event][AVDT_CCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVDT_CCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVDT_CCB_IGNORE) + { + BTTRC_AVDT_CCB_ACTION(action); + (*avdt_cb.p_ccb_act[action])(p_ccb, p_data); + } + else + { + break; + } + } +} + + +/******************************************************************************* +** +** Function avdt_ccb_by_bd +** +** Description This lookup function finds the ccb for a BD address. +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_by_bd(BD_ADDR bd_addr) +{ + tAVDT_CCB *p_ccb = &avdt_cb.ccb[0]; + int i; + + for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) + { + /* if allocated ccb has matching ccb */ + if (p_ccb->allocated && (!memcmp(p_ccb->peer_addr, bd_addr, BD_ADDR_LEN))) + { + break; + } + } + + if (i == AVDT_NUM_LINKS) + { + /* if no ccb found */ + p_ccb = NULL; + + AVDT_TRACE_DEBUG6("No ccb for addr %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avdt_ccb_alloc +** +** Description Allocate a channel control block. +** +** +** Returns pointer to the ccb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_alloc(BD_ADDR bd_addr) +{ + tAVDT_CCB *p_ccb = &avdt_cb.ccb[0]; + int i; + + for (i = 0; i < AVDT_NUM_LINKS; i++, p_ccb++) + { + if (!p_ccb->allocated) + { + p_ccb->allocated = TRUE; + memcpy(p_ccb->peer_addr, bd_addr, BD_ADDR_LEN); + GKI_init_q(&p_ccb->cmd_q); + GKI_init_q(&p_ccb->rsp_q); + p_ccb->timer_entry.param = (UINT32) p_ccb; + AVDT_TRACE_DEBUG1("avdt_ccb_alloc %d", i); + break; + } + } + + if (i == AVDT_NUM_LINKS) + { + /* out of ccbs */ + p_ccb = NULL; + AVDT_TRACE_WARNING0("Out of ccbs"); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avdt_ccb_dealloc +** +** Description Deallocate a stream control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_dealloc(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + AVDT_TRACE_DEBUG1("avdt_ccb_dealloc %d", avdt_ccb_to_idx(p_ccb)); + btu_stop_timer(&p_ccb->timer_entry); + memset(p_ccb, 0, sizeof(tAVDT_CCB)); +} + +/******************************************************************************* +** +** Function avdt_ccb_to_idx +** +** Description Given a pointer to an ccb, return its index. +** +** +** Returns Index of ccb. +** +*******************************************************************************/ +UINT8 avdt_ccb_to_idx(tAVDT_CCB *p_ccb) +{ + /* use array arithmetic to determine index */ + return (UINT8) (p_ccb - avdt_cb.ccb); +} + +/******************************************************************************* +** +** Function avdt_ccb_by_idx +** +** Description Return ccb pointer based on ccb index. +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVDT_CCB *avdt_ccb_by_idx(UINT8 idx) +{ + tAVDT_CCB *p_ccb; + + /* verify index */ + if (idx < AVDT_NUM_LINKS) + { + p_ccb = &avdt_cb.ccb[idx]; + } + else + { + p_ccb = NULL; + AVDT_TRACE_WARNING1("No ccb for idx %d", idx); + } + return p_ccb; +} + diff --git a/stack/avdt/avdt_ccb_act.c b/stack/avdt/avdt_ccb_act.c new file mode 100644 index 0000000..5719be9 --- /dev/null +++ b/stack/avdt/avdt_ccb_act.c @@ -0,0 +1,1108 @@ +/****************************************************************************** + * + * Copyright (C) 2006-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 module contains the action functions associated with the channel + * control block state machine. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "gki.h" +#include "btu.h" +#include "btm_api.h" + +/******************************************************************************* +** +** Function avdt_ccb_clear_ccb +** +** Description This function clears out certain buffers, queues, and +** other data elements of a ccb. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_ccb_clear_ccb(tAVDT_CCB *p_ccb) +{ + BT_HDR *p_buf; + + /* clear certain ccb variables */ + p_ccb->cong = FALSE; + p_ccb->ret_count = 0; + + /* free message being fragmented */ + if (p_ccb->p_curr_msg != NULL) + { + GKI_freebuf(p_ccb->p_curr_msg); + p_ccb->p_curr_msg = NULL; + } + + /* free message being reassembled */ + if (p_ccb->p_rx_msg != NULL) + { + GKI_freebuf(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + } + + /* clear out response queue */ + while ((p_buf = (BT_HDR *) GKI_dequeue(&p_ccb->rsp_q)) != NULL) + { + GKI_freebuf(p_buf); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_chan_open +** +** Description This function calls avdt_ad_open_req() to +** initiate a signaling channel connection. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG); + avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT); +} + +/******************************************************************************* +** +** Function avdt_ccb_chan_close +** +** Description This function calls avdt_ad_close_req() to close a +** signaling channel connection. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chan_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* close the transport channel used by this CCB */ + avdt_ad_close_req(AVDT_CHAN_SIG, p_ccb, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_close +** +** Description This function checks for active streams on this CCB. +** If there are none, it starts an idle timer. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + + /* see if there are any active scbs associated with this ccb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) + { + if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) + { + break; + } + } + + /* if no active scbs start idle timer */ + if (i == AVDT_NUM_SEPS) + { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_IDLE, avdt_cb.rcb.idle_tout); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_discover_cmd +** +** Description This function is called when a discover command is +** received from the peer. It gathers up the stream +** information for all allocated streams and initiates +** sending of a discover response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SEP_INFO sep_info[AVDT_NUM_SEPS]; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + + p_data->msg.discover_rsp.p_sep_info = sep_info; + p_data->msg.discover_rsp.num_seps = 0; + + /* for all allocated scbs */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) + { + if (p_scb->allocated) + { + /* copy sep info */ + sep_info[i].in_use = p_scb->in_use; + sep_info[i].seid = i + 1; + sep_info[i].media_type = p_scb->cs.media_type; + sep_info[i].tsep = p_scb->cs.tsep; + + p_data->msg.discover_rsp.num_seps++; + } + } + + /* send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_discover_rsp +** +** Description This function is called when a discover response or +** reject is received from the peer. It calls the application +** callback function with the results. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* we're done with procedure */ + p_ccb->proc_busy = FALSE; + + /* call app callback with results */ + (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_DISCOVER_CFM_EVT, + (tAVDT_CTRL *)(&p_data->msg.discover_rsp)); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_getcap_cmd +** +** Description This function is called when a get capabilities command +** is received from the peer. It retrieves the stream +** configuration for the requested stream and initiates +** sending of a get capabilities response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + + /* look up scb for seid sent to us */ + p_scb = avdt_scb_by_hdl(p_data->msg.single.seid); + + p_data->msg.svccap.p_cfg = &p_scb->cs.cfg; + + avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_getcap_rsp +** +** Description This function is called with a get capabilities response +** or reject is received from the peer. It calls the +** application callback function with the results. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* we're done with procedure */ + p_ccb->proc_busy = FALSE; + + /* call app callback with results */ + (*p_ccb->proc_cback)(0, p_ccb->peer_addr, AVDT_GETCAP_CFM_EVT, + (tAVDT_CTRL *)(&p_data->msg.svccap)); +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_start_cmd +** +** Description This function is called when a start command is received +** from the peer. It verifies that all requested streams +** are in the proper state. If so, it initiates sending of +** a start response. Otherwise it sends a start reject. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 seid; + UINT8 err_code; + + /* verify all streams in the right state */ + if ((seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_START, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &err_code)) == 0) + { + /* we're ok, send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_START_RSP_EVT, p_data); + } + else + { + /* not ok, send reject */ + p_data->msg.hdr.err_code = err_code; + p_data->msg.hdr.err_param = seid; + avdt_msg_send_rej(p_ccb, AVDT_SIG_START, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_start_rsp +** +** Description This function is called when a start response or reject +** is received from the peer. Using the SEIDs stored in the +** current command message, it sends a start response or start +** reject event to each SCB associated with the command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 event; + int i; + UINT8 *p; + tAVDT_SCB *p_scb; + + /* determine rsp or rej event */ + event = (p_data->msg.hdr.err_code == 0) ? + AVDT_SCB_MSG_START_RSP_EVT : AVDT_SCB_MSG_START_REJ_EVT; + + /* get to where seid's are stashed in current cmd */ + p = (UINT8 *)(p_ccb->p_curr_cmd + 1); + + /* little trick here; length of current command equals number of streams */ + for (i = 0; i < p_ccb->p_curr_cmd->len; i++) + { + if ((p_scb = avdt_scb_by_hdl(p[i])) != NULL) + { + avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT *) &p_data->msg); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_suspend_cmd +** +** Description This function is called when a suspend command is received +** from the peer. It verifies that all requested streams are +** in the proper state. If so, it initiates sending of a +** suspend response. Otherwise it sends a suspend reject. + +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 seid; + UINT8 err_code; + + /* verify all streams in the right state */ + if ((seid = avdt_scb_verify(p_ccb, AVDT_VERIFY_SUSPEND, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &err_code)) == 0) + { + /* we're ok, send response */ + avdt_ccb_event(p_ccb, AVDT_CCB_API_SUSPEND_RSP_EVT, p_data); + } + else + { + /* not ok, send reject */ + p_data->msg.hdr.err_code = err_code; + p_data->msg.hdr.err_param = seid; + avdt_msg_send_rej(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_hdl_suspend_rsp +** +** Description This function is called when a suspend response or reject +** is received from the peer. Using the SEIDs stored in the +** current command message, it sends a suspend response or +** suspend reject event to each SCB associated with the command. +** +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 event; + int i; + UINT8 *p; + tAVDT_SCB *p_scb; + + /* determine rsp or rej event */ + event = (p_data->msg.hdr.err_code == 0) ? + AVDT_SCB_MSG_SUSPEND_RSP_EVT : AVDT_SCB_MSG_SUSPEND_REJ_EVT; + + /* get to where seid's are stashed in current cmd */ + p = (UINT8 *)(p_ccb->p_curr_cmd + 1); + + /* little trick here; length of current command equals number of streams */ + for (i = 0; i < p_ccb->p_curr_cmd->len; i++) + { + if ((p_scb = avdt_scb_by_hdl(p[i])) != NULL) + { + avdt_scb_event(p_scb, event, (tAVDT_SCB_EVT *) &p_data->msg); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_discover_cmd +** +** Description This function is called to send a discover command to the +** peer. It copies variables needed for the procedure from +** the event to the CCB. It marks the CCB as busy and then +** sends a discover command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* store info in ccb struct */ + p_ccb->p_proc_data = p_data->discover.p_sep_info; + p_ccb->proc_cback = p_data->discover.p_cback; + p_ccb->proc_param = p_data->discover.num_seps; + + /* we're busy */ + p_ccb->proc_busy = TRUE; + + /* build and queue discover req */ + avdt_msg_send_cmd(p_ccb, NULL, AVDT_SIG_DISCOVER, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_discover_rsp +** +** Description This function is called to send a discover response to +** the peer. It takes the stream information passed in the +** event and sends a discover response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* send response */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_DISCOVER, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_getcap_cmd +** +** Description This function is called to send a get capabilities command +** to the peer. It copies variables needed for the procedure +** from the event to the CCB. It marks the CCB as busy and +** then sends a get capabilities command. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 sig_id = AVDT_SIG_GETCAP; + + /* store info in ccb struct */ + p_ccb->p_proc_data = p_data->getcap.p_cfg; + p_ccb->proc_cback = p_data->getcap.p_cback; + + /* we're busy */ + p_ccb->proc_busy = TRUE; + + /* build and queue discover req */ + if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) + sig_id = AVDT_SIG_GET_ALLCAP; + + avdt_msg_send_cmd(p_ccb, NULL, sig_id, (tAVDT_MSG *) &p_data->getcap.single); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_getcap_rsp +** +** Description This function is called to send a get capabilities response +** to the peer. It takes the stream information passed in the +** event and sends a get capabilities response. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 sig_id = AVDT_SIG_GETCAP; + + if (p_data->msg.hdr.sig_id == AVDT_SIG_GET_ALLCAP) + sig_id = AVDT_SIG_GET_ALLCAP; + + /* send response */ + avdt_msg_send_rsp(p_ccb, sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_start_cmd +** +** Description This function is called to send a start command to the +** peer. It verifies that all requested streams are in the +** proper state. If so, it sends a start command. Otherwise +** send ourselves back a start reject. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb; + tAVDT_MSG avdt_msg; + UINT8 seid_list[AVDT_NUM_SEPS]; + + /* make copy of our seid list */ + memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); + + /* verify all streams in the right state */ + if ((avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_OPEN, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code)) == 0) + { + /* set peer seid list in messsage */ + avdt_scb_peer_seid_list(&p_data->msg.multi); + + /* send command */ + avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_START, &p_data->msg); + } + else + { + /* failed; send ourselves a reject for each stream */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) + { + if ((p_scb = avdt_scb_by_hdl(seid_list[i])) != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_MSG_START_REJ_EVT, (tAVDT_SCB_EVT *) &avdt_msg.hdr); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_start_rsp +** +** Description This function is called to send a start response to the +** peer. It takes the stream information passed in the event +** and sends a start response. Then it sends a start event +** to the SCB for each stream. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + int i; + + /* send response message */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_START, &p_data->msg); + + /* send start event to each scb */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i])) != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_MSG_START_CMD_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_suspend_cmd +** +** Description This function is called to send a suspend command to the +** peer. It verifies that all requested streams are in the +** proper state. If so, it sends a suspend command. +** Otherwise it calls the callback function for each requested +** stream and sends a suspend confirmation with failure. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb; + tAVDT_MSG avdt_msg; + UINT8 seid_list[AVDT_NUM_SEPS]; + + /* make copy of our seid list */ + memcpy(seid_list, p_data->msg.multi.seid_list, p_data->msg.multi.num_seps); + + /* verify all streams in the right state */ + if ((avdt_msg.hdr.err_param = avdt_scb_verify(p_ccb, AVDT_VERIFY_STREAMING, p_data->msg.multi.seid_list, + p_data->msg.multi.num_seps, &avdt_msg.hdr.err_code)) == 0) + { + /* set peer seid list in messsage */ + avdt_scb_peer_seid_list(&p_data->msg.multi); + + /* send command */ + avdt_msg_send_cmd(p_ccb, seid_list, AVDT_SIG_SUSPEND, &p_data->msg); + } + else + { + /* failed; send ourselves a reject for each stream */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) + { + if ((p_scb = avdt_scb_by_hdl(seid_list[i])) != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_REJ_EVT, (tAVDT_SCB_EVT *) &avdt_msg.hdr); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_suspend_rsp +** +** Description This function is called to send a suspend response to the +** peer. It takes the stream information passed in the event +** and sends a suspend response. Then it sends a suspend event +** to the SCB for each stream. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_SCB *p_scb; + int i; + + /* send response message */ + avdt_msg_send_rsp(p_ccb, AVDT_SIG_SUSPEND, &p_data->msg); + + /* send start event to each scb */ + for (i = 0; i < p_data->msg.multi.num_seps; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_data->msg.multi.seid_list[i])) != NULL) + { + avdt_scb_event(p_scb, AVDT_SCB_MSG_SUSPEND_CMD_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_clear_cmds +** +** Description This function is called when the signaling channel is +** closed to clean up any pending commands. For each pending +** command in the command queue, it frees the command and +** calls the application callback function indicating failure. +** Certain CCB variables are also initialized. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_clear_cmds(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + int i; + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + UINT8 err_code = AVDT_ERR_CONNECT; + + /* clear the ccb */ + avdt_ccb_clear_ccb(p_ccb); + + /* clear out command queue; this is a little tricky here; we need + ** to handle the case where there is a command on deck in p_curr_cmd, + ** plus we need to clear out the queue + */ + do + { + /* we know p_curr_cmd = NULL after this */ + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* set up next message */ + p_ccb->p_curr_cmd = (BT_HDR *) GKI_dequeue(&p_ccb->cmd_q); + + } while (p_ccb->p_curr_cmd != NULL); + + /* send a CC_CLOSE_EVT any active scbs associated with this ccb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) + { + if ((p_scb->allocated) && (p_scb->p_ccb == p_ccb)) + { + avdt_scb_event(p_scb, AVDT_SCB_CC_CLOSE_EVT, NULL); + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_cmd_fail +** +** Description This function is called when there is a response timeout. +** The currently pending command is freed and we fake a +** reject message back to ourselves. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_cmd_fail(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_MSG msg; + UINT8 evt; + tAVDT_SCB *p_scb; + + if (p_ccb->p_curr_cmd != NULL) + { + /* set up data */ + msg.hdr.err_code = p_data->err_code; + msg.hdr.err_param = 0; + msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* pretend that we received a rej message */ + evt = avdt_msg_rej_2_evt[p_ccb->p_curr_cmd->event - 1]; + + if (evt & AVDT_CCB_MKR) + { + avdt_ccb_event(p_ccb, (UINT8) (evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); + } + else + { + /* we get the scb out of the current cmd */ + p_scb = avdt_scb_by_hdl(*((UINT8 *)(p_ccb->p_curr_cmd + 1))); + if (p_scb != NULL) + { + avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg); + } + } + + GKI_freebuf(p_ccb->p_curr_cmd); + p_ccb->p_curr_cmd = NULL; + } +} + +/******************************************************************************* +** +** Function avdt_ccb_free_cmd +** +** Description This function is called when a response is received for a +** currently pending command. The command is freed. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_free_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + if (p_ccb->p_curr_cmd != NULL) + { + GKI_freebuf(p_ccb->p_curr_cmd); + p_ccb->p_curr_cmd = NULL; + } +} + +/******************************************************************************* +** +** Function avdt_ccb_cong_state +** +** Description This function is called to set the congestion state for +** the CCB. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_cong_state(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + p_ccb->cong = p_data->llcong; +} + +/******************************************************************************* +** +** Function avdt_ccb_ret_cmd +** +** Description This function is called to retransmit the currently +** pending command. The retransmission count is incremented. +** If the count reaches the maximum number of retransmissions, +** the event is treated as a response timeout. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ret_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 err_code = AVDT_ERR_TIMEOUT; + BT_HDR *p_msg; + + p_ccb->ret_count++; + if (p_ccb->ret_count == AVDT_RET_MAX) + { + /* command failed */ + p_ccb->ret_count = 0; + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* go to next queued command */ + avdt_ccb_snd_cmd(p_ccb, p_data); + } + else + { + /* if command pending and we're not congested and not sending a fragment */ + if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd != NULL)) + { + /* make copy of message in p_curr_cmd and send it */ + if ((p_msg = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID)) != NULL) + { + memcpy(p_msg, p_ccb->p_curr_cmd, + (sizeof(BT_HDR) + p_ccb->p_curr_cmd->offset + p_ccb->p_curr_cmd->len)); + avdt_msg_send(p_ccb, p_msg); + } + } + + /* restart timer */ + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RET, avdt_cb.rcb.ret_tout); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_cmd +** +** Description This function is called the send the next command, +** if any, in the command queue. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + BT_HDR *p_msg; + + /* do we have commands to send? send next command; make sure we're clear; + ** not congested, not sending fragment, not waiting for response + */ + if ((!p_ccb->cong) && (p_ccb->p_curr_msg == NULL) && (p_ccb->p_curr_cmd == NULL)) + { + if ((p_msg = (BT_HDR *) GKI_dequeue(&p_ccb->cmd_q)) != NULL) + { + /* make a copy of buffer in p_curr_cmd */ + if ((p_ccb->p_curr_cmd = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID)) != NULL) + { + memcpy(p_ccb->p_curr_cmd, p_msg, (sizeof(BT_HDR) + p_msg->offset + p_msg->len)); + + avdt_msg_send(p_ccb, p_msg); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_ccb_snd_msg +** +** Description +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_snd_msg(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + BT_HDR *p_msg; + + /* if not congested */ + if (!p_ccb->cong) + { + /* are we sending a fragmented message? continue sending fragment */ + if (p_ccb->p_curr_msg != NULL) + { + avdt_msg_send(p_ccb, NULL); + } + /* do we have responses to send? send them */ + else if (!GKI_queue_is_empty(&p_ccb->rsp_q)) + { + while ((p_msg = (BT_HDR *) GKI_dequeue(&p_ccb->rsp_q)) != NULL) + { + if (avdt_msg_send(p_ccb, p_msg) == TRUE) + { + /* break out if congested */ + break; + } + } + } + + /* do we have commands to send? send next command */ + avdt_ccb_snd_cmd(p_ccb, NULL); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_set_reconn +** +** Description This function is called to enable a reconnect attempt when +** a channel transitions from closing to idle state. It sets +** the reconn variable to TRUE. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + p_ccb->reconn = TRUE; +} + +/******************************************************************************* +** +** Function avdt_ccb_clr_reconn +** +** Description This function is called to clear the reconn variable. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_clr_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + p_ccb->reconn = FALSE; +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_reconn +** +** Description This function is called to check if a reconnect attempt +** is enabled. If enabled, it sends an AVDT_CCB_UL_OPEN_EVT +** to the CCB. If disabled, the CCB is deallocated. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + UINT8 err_code = AVDT_ERR_CONNECT; + + if (p_ccb->reconn) + { + p_ccb->reconn = FALSE; + + /* clear out ccb */ + avdt_ccb_clear_ccb(p_ccb); + + /* clear out current command, if any */ + avdt_ccb_cmd_fail(p_ccb, (tAVDT_CCB_EVT *) &err_code); + + /* reopen the signaling channel */ + avdt_ccb_event(p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); + } + else + { + avdt_ccb_ll_closed(p_ccb, NULL); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_chk_timer +** +** Description This function stops the CCB timer if the idle timer is +** running. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_chk_timer(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + if (p_ccb->timer_entry.event == BTU_TTYPE_AVDT_CCB_IDLE) + { + btu_stop_timer(&p_ccb->timer_entry); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_set_conn +** +** Description Set CCB variables associated with AVDT_ConnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* save callback */ + p_ccb->p_conn_cback = p_data->connect.p_cback; + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_data->connect.sec_mask, + AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG); +} + +/******************************************************************************* +** +** Function avdt_ccb_set_disconn +** +** Description Set CCB variables associated with AVDT_DisconnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_set_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* + AVDT_TRACE_EVENT2("avdt_ccb_set_disconn:conn:x%x, api:x%x", + p_ccb->p_conn_cback, p_data->disconnect.p_cback); + */ + /* save callback */ + if (p_data->disconnect.p_cback) + p_ccb->p_conn_cback = p_data->disconnect.p_cback; +} + +/******************************************************************************* +** +** Function avdt_ccb_do_disconn +** +** Description Do action associated with AVDT_DisconnectReq(). +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_do_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + /* clear any pending commands */ + avdt_ccb_clear_cmds(p_ccb, NULL); + + /* close channel */ + avdt_ccb_chan_close(p_ccb, NULL); +} + +/******************************************************************************* +** +** Function avdt_ccb_ll_closed +** +** Description Clear commands from and deallocate CCB. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ll_closed(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CTRL_CBACK *p_cback; + BD_ADDR bd_addr; + tAVDT_CTRL avdt_ctrl; + + /* clear any pending commands */ + avdt_ccb_clear_cmds(p_ccb, NULL); + + /* save callback pointer, bd addr */ + p_cback = p_ccb->p_conn_cback; + if (!p_cback) + p_cback = avdt_cb.p_conn_cback; + memcpy(bd_addr, p_ccb->peer_addr, BD_ADDR_LEN); + + /* dealloc ccb */ + avdt_ccb_dealloc(p_ccb, NULL); + + /* call callback */ + if (p_cback) + { + avdt_ctrl.hdr.err_code = 0; + (*p_cback)(0, bd_addr, AVDT_DISCONNECT_IND_EVT, &avdt_ctrl); + } +} + +/******************************************************************************* +** +** Function avdt_ccb_ll_opened +** +** Description Call callback on open. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_ccb_ll_opened(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + + p_ccb->ll_opened = TRUE; + + if (!p_ccb->p_conn_cback) + p_ccb->p_conn_cback = avdt_cb.p_conn_cback; + + /* call callback */ + if (p_ccb->p_conn_cback) + { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = p_data->msg.hdr.err_param; + (*p_ccb->p_conn_cback)(0, p_ccb->peer_addr, AVDT_CONNECT_IND_EVT, &avdt_ctrl); + } +} diff --git a/stack/avdt/avdt_defs.h b/stack/avdt/avdt_defs.h new file mode 100644 index 0000000..b6dbbc4 --- /dev/null +++ b/stack/avdt/avdt_defs.h @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 contains constants definitions and other information from the AVDTP + * specification. This file is intended for use internal to AVDT only. + * + ******************************************************************************/ +#ifndef AVDT_DEFS_H +#define AVDT_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* signalling packet type */ +#define AVDT_PKT_TYPE_SINGLE 0 /* single packet */ +#define AVDT_PKT_TYPE_START 1 /* start packet */ +#define AVDT_PKT_TYPE_CONT 2 /* continue packet */ +#define AVDT_PKT_TYPE_END 3 /* end packet */ + +/* signalling message type */ +#define AVDT_MSG_TYPE_CMD 0 /* command */ +#define AVDT_MSG_TYPE_GRJ 1 /* general reject */ +#define AVDT_MSG_TYPE_RSP 2 /* response accept */ +#define AVDT_MSG_TYPE_REJ 3 /* response reject */ + +/* signalling messages */ +#define AVDT_SIG_DISCOVER 1 /* discover */ +#define AVDT_SIG_GETCAP 2 /* get capabilities */ +#define AVDT_SIG_SETCONFIG 3 /* set configuration */ +#define AVDT_SIG_GETCONFIG 4 /* get configuration */ +#define AVDT_SIG_RECONFIG 5 /* reconfigure */ +#define AVDT_SIG_OPEN 6 /* open */ +#define AVDT_SIG_START 7 /* start */ +#define AVDT_SIG_CLOSE 8 /* close */ +#define AVDT_SIG_SUSPEND 9 /* suspend */ +#define AVDT_SIG_ABORT 10 /* abort */ +#define AVDT_SIG_SECURITY 11 /* security control */ +#define AVDT_SIG_GET_ALLCAP 12 /* get all capabilities */ +#define AVDT_SIG_DELAY_RPT 13 /* delay report */ + +/* maximum signal value */ +#define AVDT_SIG_MAX AVDT_SIG_DELAY_RPT + +/* used for general reject */ +#define AVDT_SIG_NONE 0 + +/* some maximum and minimum sizes of signalling messages */ +#define AVDT_DISCOVER_REQ_MIN 1 +#define AVDT_DISCOVER_REQ_MAX 124 + +/* service category information element field values */ +#define AVDT_CAT_TRANS 1 /* Media Transport */ +#define AVDT_CAT_REPORT 2 /* Reporting */ +#define AVDT_CAT_RECOV 3 /* Recovery */ +#define AVDT_CAT_PROTECT 4 /* Content Protection */ +#define AVDT_CAT_HDRCMP 5 /* Header Compression */ +#define AVDT_CAT_MUX 6 /* Multiplexing */ +#define AVDT_CAT_CODEC 7 /* Media Codec */ +#define AVDT_CAT_DELAY_RPT 8 /* Delay Reporting */ +#define AVDT_CAT_MAX_CUR AVDT_CAT_DELAY_RPT + +/* min/max lengths of service category information elements */ +#define AVDT_LEN_TRANS_MIN 0 +#define AVDT_LEN_REPORT_MIN 0 +#define AVDT_LEN_RECOV_MIN 3 +#define AVDT_LEN_PROTECT_MIN 2 +#define AVDT_LEN_HDRCMP_MIN 1 +#define AVDT_LEN_MUX_MIN 3 +#define AVDT_LEN_CODEC_MIN 2 +#define AVDT_LEN_DELAY_RPT_MIN 0 + +#define AVDT_LEN_TRANS_MAX 0 +#define AVDT_LEN_REPORT_MAX 0 +#define AVDT_LEN_RECOV_MAX 3 +#define AVDT_LEN_PROTECT_MAX 255 +#define AVDT_LEN_HDRCMP_MAX 1 +#define AVDT_LEN_MUX_MAX 7 +#define AVDT_LEN_CODEC_MAX 255 +#define AVDT_LEN_DELAY_RPT_MAX 0 + +/* minimum possible size of configuration or capabilities data */ +#define AVDT_LEN_CFG_MIN 2 + +/* minimum and maximum lengths for different message types */ +#define AVDT_LEN_SINGLE 1 +#define AVDT_LEN_SETCONFIG_MIN 2 +#define AVDT_LEN_RECONFIG_MIN 1 +#define AVDT_LEN_MULTI_MIN 1 +#define AVDT_LEN_SECURITY_MIN 1 +#define AVDT_LEN_DELAY_RPT 3 + +/* header lengths for different packet types */ +#define AVDT_LEN_TYPE_SINGLE 2 /* single packet */ +#define AVDT_LEN_TYPE_START 3 /* start packet */ +#define AVDT_LEN_TYPE_CONT 1 /* continue packet */ +#define AVDT_LEN_TYPE_END 1 /* end packet */ + +/* length of general reject message */ +#define AVDT_LEN_GEN_REJ 2 + +/* recovery service capabilities information elements */ +#define AVDT_RECOV_MRWS_MIN 0x01 /* min value for maximum recovery window */ +#define AVDT_RECOV_MRWS_MAX 0x18 /* max value for maximum recovery window */ +#define AVDT_RECOV_MNMP_MIN 0x01 /* min value for maximum number of media packets */ +#define AVDT_RECOV_MNMP_MAX 0x18 /* max value for maximum number of media packets */ + +/* SEID value range */ +#define AVDT_SEID_MIN 0x01 +#define AVDT_SEID_MAX 0x3E + +/* first byte of media packet header */ +#define AVDT_MEDIA_OCTET1 0x80 + +/* for adaptation layer header */ +#define AVDT_ALH_LCODE_MASK 0x03 /* coding of length field */ +#define AVDT_ALH_LCODE_NONE 0x00 /* No length field present. Take length from l2cap */ +#define AVDT_ALH_LCODE_16BIT 0x01 /* 16bit length field */ +#define AVDT_ALH_LCODE_9BITM0 0x02 /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */ +#define AVDT_ALH_LCODE_9BITM1 0x03 /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */ + +#define AVDT_ALH_FRAG_MASK 0x04 /* set this for continuation packet */ + +/***************************************************************************** +** message parsing and building macros +*****************************************************************************/ + +#define AVDT_MSG_PRS_HDR(p, lbl, pkt, msg) \ + lbl = *(p) >> 4; \ + pkt = (*(p) >> 2) & 0x03; \ + msg = *(p)++ & 0x03; + +#define AVDT_MSG_PRS_DISC(p, seid, in_use, type, tsep) \ + seid = *(p) >> 2; \ + in_use = (*(p)++ >> 1) & 0x01; \ + type = *(p) >> 4; \ + tsep = (*(p)++ >> 3) & 0x01; + +#define AVDT_MSG_PRS_SIG(p, sig) \ + sig = *(p)++ & 0x3F; + +#define AVDT_MSG_PRS_SEID(p, seid) \ + seid = *(p)++ >> 2; + +#define AVDT_MSG_PRS_PKT_TYPE(p, pkt) \ + pkt = (*(p) >> 2) & 0x03; + +#define AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc) \ + o_v = *(p) >> 6; \ + o_p = (*(p) >> 5) & 0x01; \ + o_x = (*(p) >> 4) & 0x01; \ + o_cc = *(p)++ & 0x0F; + +#define AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc) \ + o_v = *(p) >> 6; \ + o_p = (*(p) >> 5) & 0x01; \ + o_cc = *(p)++ & 0x1F; + +#define AVDT_MSG_PRS_M_PT(p, m_pt, marker) \ + marker = *(p) >> 7; \ + m_pt = *(p)++ & 0x7F; + +#define AVDT_MSG_BLD_HDR(p, lbl, pkt, msg) \ + *(p)++ = (UINT8) ((lbl) << 4) | ((pkt) << 2) | (msg); + +#define AVDT_MSG_BLD_DISC(p, seid, in_use, type, tsep) \ + *(p)++ = (UINT8) (((seid) << 2) | ((in_use) << 1)); \ + *(p)++ = (UINT8) (((type) << 4) | ((tsep) << 3)); + +#define AVDT_MSG_BLD_SIG(p, sig) \ + *(p)++ = (UINT8) (sig); + +#define AVDT_MSG_BLD_SEID(p, seid) \ + *(p)++ = (UINT8) ((seid) << 2); + +#define AVDT_MSG_BLD_ERR(p, err) \ + *(p)++ = (UINT8) (err); + +#define AVDT_MSG_BLD_PARAM(p, param) \ + *(p)++ = (UINT8) (param); + +#define AVDT_MSG_BLD_NOSP(p, nosp) \ + *(p)++ = (UINT8) (nosp); + +#endif /* AVDT_DEFS_H */ + diff --git a/stack/avdt/avdt_int.h b/stack/avdt/avdt_int.h new file mode 100644 index 0000000..9b101fc --- /dev/null +++ b/stack/avdt/avdt_int.h @@ -0,0 +1,744 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains interfaces which are internal to AVDTP. + * + ******************************************************************************/ +#ifndef AVDT_INT_H +#define AVDT_INT_H + +#include "gki.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_defs.h" +#include "l2c_api.h" +#include "btm_api.h" + +#ifndef AVDT_DEBUG +#if (!defined BTTRC_INCLUDED || BTTRC_INCLUDED == FALSE) +#define AVDT_DEBUG TRUE +#else +#define AVDT_DEBUG FALSE +#endif +#endif + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* channel types */ +enum { + AVDT_CHAN_SIG, /* signaling channel */ + AVDT_CHAN_MEDIA, /* media channel */ +#if AVDT_REPORTING == TRUE + AVDT_CHAN_REPORT, /* reporting channel */ +#endif + AVDT_CHAN_NUM_TYPES +}; + +/* protocol service capabilities of this AVDTP implementation */ +/* for now multiplexing will be used only for fragmentation */ +#if ((AVDT_MULTIPLEXING == TRUE) && (AVDT_REPORTING == TRUE)) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_REPORT) +#else /* AVDT_MULTIPLEXING && AVDT_REPORTING */ + +#if (AVDT_MULTIPLEXING == TRUE) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_MUX) +#else /* AVDT_MULTIPLEXING */ + +#if (AVDT_REPORTING == TRUE) +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS | AVDT_PSC_REPORT) +#else /* AVDT_REPORTING */ +#define AVDT_PSC (AVDT_PSC_TRANS | AVDT_PSC_DELAY_RPT) +#define AVDT_LEG_PSC (AVDT_PSC_TRANS) +#endif /* AVDT_REPORTING */ + +#endif /* AVDT_MULTIPLEXING */ + +#endif /* AVDT_MULTIPLEXING && AVDT_REPORTING */ + +/* initiator/acceptor signaling roles */ +#define AVDT_CLOSE_ACP 0 +#define AVDT_CLOSE_INT 1 +#define AVDT_OPEN_ACP 2 +#define AVDT_OPEN_INT 3 + +/* states for avdt_scb_verify */ +#define AVDT_VERIFY_OPEN 0 +#define AVDT_VERIFY_STREAMING 1 +#define AVDT_VERIFY_SUSPEND 2 +#define AVDT_VERIFY_START 3 + +/* to distinguish CCB events from SCB events */ +#define AVDT_CCB_MKR 0x80 + +/* offset where AVDTP signaling message header starts in message */ +#define AVDT_HDR_OFFSET (L2CAP_MIN_OFFSET + AVDT_NUM_SEPS) + +/* offset where AVDTP signaling message content starts; +** use the size of a start header since it's the largest possible +** layout of signaling message in a buffer is: +** +** | BT_HDR | SCB handles | L2CAP + HCI header | AVDTP header | data ... | +** +** Note that we "hide" the scb handles at the top of the message buffer. +*/ +#define AVDT_MSG_OFFSET (L2CAP_MIN_OFFSET + AVDT_NUM_SEPS + AVDT_LEN_TYPE_START) + +/* scb transport channel connect timeout value */ +#define AVDT_SCB_TC_CONN_TOUT 10 + +/* scb transport channel disconnect timeout value */ +#define AVDT_SCB_TC_DISC_TOUT 10 + +/* maximum number of command retransmissions */ +#ifndef AVDT_RET_MAX +#define AVDT_RET_MAX 1 +#endif + + +/* ccb state machine states */ +enum { + AVDT_CCB_IDLE_ST, + AVDT_CCB_OPENING_ST, + AVDT_CCB_OPEN_ST, + AVDT_CCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVDT_CCB_CHAN_OPEN, + AVDT_CCB_CHAN_CLOSE, + AVDT_CCB_CHK_CLOSE, + AVDT_CCB_HDL_DISCOVER_CMD, + AVDT_CCB_HDL_DISCOVER_RSP, + AVDT_CCB_HDL_GETCAP_CMD, + AVDT_CCB_HDL_GETCAP_RSP, + AVDT_CCB_HDL_START_CMD, + AVDT_CCB_HDL_START_RSP, + AVDT_CCB_HDL_SUSPEND_CMD, + AVDT_CCB_HDL_SUSPEND_RSP, + AVDT_CCB_SND_DISCOVER_CMD, + AVDT_CCB_SND_DISCOVER_RSP, + AVDT_CCB_SND_GETCAP_CMD, + AVDT_CCB_SND_GETCAP_RSP, + AVDT_CCB_SND_START_CMD, + AVDT_CCB_SND_START_RSP, + AVDT_CCB_SND_SUSPEND_CMD, + AVDT_CCB_SND_SUSPEND_RSP, + AVDT_CCB_CLEAR_CMDS, + AVDT_CCB_CMD_FAIL, + AVDT_CCB_FREE_CMD, + AVDT_CCB_CONG_STATE, + AVDT_CCB_RET_CMD, + AVDT_CCB_SND_CMD, + AVDT_CCB_SND_MSG, + AVDT_CCB_SET_RECONN, + AVDT_CCB_CLR_RECONN, + AVDT_CCB_CHK_RECONN, + AVDT_CCB_CHK_TIMER, + AVDT_CCB_SET_CONN, + AVDT_CCB_SET_DISCONN, + AVDT_CCB_DO_DISCONN, + AVDT_CCB_LL_CLOSED, + AVDT_CCB_LL_OPENED, + AVDT_CCB_DEALLOC, + AVDT_CCB_NUM_ACTIONS +}; + +#define AVDT_CCB_IGNORE AVDT_CCB_NUM_ACTIONS + +/* ccb state machine events */ +enum { + AVDT_CCB_API_DISCOVER_REQ_EVT, + AVDT_CCB_API_GETCAP_REQ_EVT, + AVDT_CCB_API_START_REQ_EVT, + AVDT_CCB_API_SUSPEND_REQ_EVT, + AVDT_CCB_API_DISCOVER_RSP_EVT, + AVDT_CCB_API_GETCAP_RSP_EVT, + AVDT_CCB_API_START_RSP_EVT, + AVDT_CCB_API_SUSPEND_RSP_EVT, + AVDT_CCB_API_CONNECT_REQ_EVT, + AVDT_CCB_API_DISCONNECT_REQ_EVT, + AVDT_CCB_MSG_DISCOVER_CMD_EVT, + AVDT_CCB_MSG_GETCAP_CMD_EVT, + AVDT_CCB_MSG_START_CMD_EVT, + AVDT_CCB_MSG_SUSPEND_CMD_EVT, + AVDT_CCB_MSG_DISCOVER_RSP_EVT, + AVDT_CCB_MSG_GETCAP_RSP_EVT, + AVDT_CCB_MSG_START_RSP_EVT, + AVDT_CCB_MSG_SUSPEND_RSP_EVT, + AVDT_CCB_RCVRSP_EVT, + AVDT_CCB_SENDMSG_EVT, + AVDT_CCB_RET_TOUT_EVT, + AVDT_CCB_RSP_TOUT_EVT, + AVDT_CCB_IDLE_TOUT_EVT, + AVDT_CCB_UL_OPEN_EVT, + AVDT_CCB_UL_CLOSE_EVT, + AVDT_CCB_LL_OPEN_EVT, + AVDT_CCB_LL_CLOSE_EVT, + AVDT_CCB_LL_CONG_EVT +}; + + +/* scb state machine states; these state values are private to this module so +** the scb state cannot be read or set by actions functions +*/ +enum { + AVDT_SCB_IDLE_ST, + AVDT_SCB_CONF_ST, + AVDT_SCB_OPENING_ST, + AVDT_SCB_OPEN_ST, + AVDT_SCB_STREAM_ST, + AVDT_SCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVDT_SCB_HDL_ABORT_CMD, + AVDT_SCB_HDL_ABORT_RSP, + AVDT_SCB_HDL_CLOSE_CMD, + AVDT_SCB_HDL_CLOSE_RSP, + AVDT_SCB_HDL_GETCONFIG_CMD, + AVDT_SCB_HDL_GETCONFIG_RSP, + AVDT_SCB_HDL_OPEN_CMD, + AVDT_SCB_HDL_OPEN_REJ, + AVDT_SCB_HDL_OPEN_RSP, + AVDT_SCB_HDL_PKT, + AVDT_SCB_DROP_PKT, + AVDT_SCB_HDL_RECONFIG_CMD, + AVDT_SCB_HDL_RECONFIG_RSP, + AVDT_SCB_HDL_SECURITY_CMD, + AVDT_SCB_HDL_SECURITY_RSP, + AVDT_SCB_HDL_SETCONFIG_CMD, + AVDT_SCB_HDL_SETCONFIG_REJ, + AVDT_SCB_HDL_SETCONFIG_RSP, + AVDT_SCB_HDL_START_CMD, + AVDT_SCB_HDL_START_RSP, + AVDT_SCB_HDL_SUSPEND_CMD, + AVDT_SCB_HDL_SUSPEND_RSP, + AVDT_SCB_HDL_TC_CLOSE, +#if AVDT_REPORTING == TRUE + AVDT_SCB_HDL_TC_CLOSE_STO, +#endif + AVDT_SCB_HDL_TC_OPEN, +#if AVDT_REPORTING == TRUE + AVDT_SCB_HDL_TC_OPEN_STO, +#endif + AVDT_SCB_SND_DELAY_RPT_REQ, + AVDT_SCB_HDL_DELAY_RPT_CMD, + AVDT_SCB_HDL_DELAY_RPT_RSP, + AVDT_SCB_HDL_WRITE_REQ, + AVDT_SCB_SND_ABORT_REQ, + AVDT_SCB_SND_ABORT_RSP, + AVDT_SCB_SND_CLOSE_REQ, + AVDT_SCB_SND_STREAM_CLOSE, + AVDT_SCB_SND_CLOSE_RSP, + AVDT_SCB_SND_GETCONFIG_REQ, + AVDT_SCB_SND_GETCONFIG_RSP, + AVDT_SCB_SND_OPEN_REQ, + AVDT_SCB_SND_OPEN_RSP, + AVDT_SCB_SND_RECONFIG_REQ, + AVDT_SCB_SND_RECONFIG_RSP, + AVDT_SCB_SND_SECURITY_REQ, + AVDT_SCB_SND_SECURITY_RSP, + AVDT_SCB_SND_SETCONFIG_REQ, + AVDT_SCB_SND_SETCONFIG_REJ, + AVDT_SCB_SND_SETCONFIG_RSP, + AVDT_SCB_SND_TC_CLOSE, + AVDT_SCB_CB_ERR, + AVDT_SCB_CONG_STATE, + AVDT_SCB_REJ_STATE, + AVDT_SCB_REJ_IN_USE, + AVDT_SCB_REJ_NOT_IN_USE, + AVDT_SCB_SET_REMOVE, + AVDT_SCB_FREE_PKT, + AVDT_SCB_CLR_PKT, + AVDT_SCB_CHK_SND_PKT, + AVDT_SCB_TC_TIMER, + AVDT_SCB_CLR_VARS, + AVDT_SCB_DEALLOC, + AVDT_SCB_NUM_ACTIONS +}; + +#define AVDT_SCB_IGNORE AVDT_SCB_NUM_ACTIONS + +/* scb state machine events */ +enum { + AVDT_SCB_API_REMOVE_EVT, + AVDT_SCB_API_WRITE_REQ_EVT, + AVDT_SCB_API_GETCONFIG_REQ_EVT, + AVDT_SCB_API_DELAY_RPT_REQ_EVT, + AVDT_SCB_API_SETCONFIG_REQ_EVT, + AVDT_SCB_API_OPEN_REQ_EVT, + AVDT_SCB_API_CLOSE_REQ_EVT, + AVDT_SCB_API_RECONFIG_REQ_EVT, + AVDT_SCB_API_SECURITY_REQ_EVT, + AVDT_SCB_API_ABORT_REQ_EVT, + AVDT_SCB_API_GETCONFIG_RSP_EVT, + AVDT_SCB_API_SETCONFIG_RSP_EVT, + AVDT_SCB_API_SETCONFIG_REJ_EVT, + AVDT_SCB_API_OPEN_RSP_EVT, + AVDT_SCB_API_CLOSE_RSP_EVT, + AVDT_SCB_API_RECONFIG_RSP_EVT, + AVDT_SCB_API_SECURITY_RSP_EVT, + AVDT_SCB_API_ABORT_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_CMD_EVT, + AVDT_SCB_MSG_GETCONFIG_CMD_EVT, + AVDT_SCB_MSG_OPEN_CMD_EVT, + AVDT_SCB_MSG_START_CMD_EVT, + AVDT_SCB_MSG_SUSPEND_CMD_EVT, + AVDT_SCB_MSG_CLOSE_CMD_EVT, + AVDT_SCB_MSG_ABORT_CMD_EVT, + AVDT_SCB_MSG_RECONFIG_CMD_EVT, + AVDT_SCB_MSG_SECURITY_CMD_EVT, + AVDT_SCB_MSG_DELAY_RPT_CMD_EVT, + AVDT_SCB_MSG_DELAY_RPT_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_RSP_EVT, + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, + AVDT_SCB_MSG_OPEN_RSP_EVT, + AVDT_SCB_MSG_START_RSP_EVT, + AVDT_SCB_MSG_SUSPEND_RSP_EVT, + AVDT_SCB_MSG_CLOSE_RSP_EVT, + AVDT_SCB_MSG_ABORT_RSP_EVT, + AVDT_SCB_MSG_RECONFIG_RSP_EVT, + AVDT_SCB_MSG_SECURITY_RSP_EVT, + AVDT_SCB_MSG_SETCONFIG_REJ_EVT, + AVDT_SCB_MSG_OPEN_REJ_EVT, + AVDT_SCB_MSG_START_REJ_EVT, + AVDT_SCB_MSG_SUSPEND_REJ_EVT, + AVDT_SCB_TC_TOUT_EVT, + AVDT_SCB_TC_OPEN_EVT, + AVDT_SCB_TC_CLOSE_EVT, + AVDT_SCB_TC_CONG_EVT, + AVDT_SCB_TC_DATA_EVT, + AVDT_SCB_CC_CLOSE_EVT +}; + +/* adaption layer number of stream routing table entries */ +#if AVDT_REPORTING == TRUE +/* 2 channels(1 media, 1 report) for each SEP and one for signalling */ +#define AVDT_NUM_RT_TBL ((AVDT_NUM_SEPS<<1) + 1) +#else +#define AVDT_NUM_RT_TBL (AVDT_NUM_SEPS + 1) +#endif + +/* adaption layer number of transport channel table entries - moved to target.h +#define AVDT_NUM_TC_TBL (AVDT_NUM_SEPS + AVDT_NUM_LINKS) */ + +/* "states" used in transport channel table */ +#define AVDT_AD_ST_UNUSED 0 /* Unused - unallocated */ +#define AVDT_AD_ST_IDLE 1 /* No connection */ +#define AVDT_AD_ST_ACP 2 /* Waiting to accept a connection */ +#define AVDT_AD_ST_INT 3 /* Initiating a connection */ +#define AVDT_AD_ST_CONN 4 /* Waiting for connection confirm */ +#define AVDT_AD_ST_CFG 5 /* Waiting for configuration complete */ +#define AVDT_AD_ST_OPEN 6 /* Channel opened */ +#define AVDT_AD_ST_SEC_INT 7 /* Security process as INT */ +#define AVDT_AD_ST_SEC_ACP 8 /* Security process as ACP */ + +/* Configuration flags. tAVDT_TC_TBL.cfg_flags */ +#define AVDT_L2C_CFG_IND_DONE (1<<0) +#define AVDT_L2C_CFG_CFM_DONE (1<<1) +#define AVDT_L2C_CFG_CONN_INT (1<<2) +#define AVDT_L2C_CFG_CONN_ACP (1<<3) + + +/* result code for avdt_ad_write_req() (L2CA_DataWrite()) */ +#define AVDT_AD_FAILED L2CAP_DW_FAILED /* FALSE */ +#define AVDT_AD_SUCCESS L2CAP_DW_SUCCESS /* TRUE */ +#define AVDT_AD_CONGESTED L2CAP_DW_CONGESTED /* 2 */ + +/***************************************************************************** +** data types +*****************************************************************************/ + +/* msg union of all message parameter types */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_EVT_HDR single; + tAVDT_SETCONFIG config_cmd; + tAVDT_CONFIG reconfig_cmd; + tAVDT_MULTI multi; + tAVDT_SECURITY security_cmd; + tAVDT_DISCOVER discover_rsp; + tAVDT_CONFIG svccap; + tAVDT_SECURITY security_rsp; + tAVDT_DELAY_RPT delay_rpt_cmd; +} tAVDT_MSG; + +/* data type for AVDT_CCB_API_DISCOVER_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; + tAVDT_SEP_INFO *p_sep_info; + UINT8 num_seps; +} tAVDT_CCB_API_DISCOVER; + +/* data type for AVDT_CCB_API_GETCAP_REQ_EVT */ +typedef struct { + tAVDT_EVT_HDR single; + tAVDT_CTRL_CBACK *p_cback; + tAVDT_CFG *p_cfg; +} tAVDT_CCB_API_GETCAP; + +/* data type for AVDT_CCB_API_CONNECT_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; + UINT8 sec_mask; +} tAVDT_CCB_API_CONNECT; + +/* data type for AVDT_CCB_API_DISCONNECT_REQ_EVT */ +typedef struct { + tAVDT_CTRL_CBACK *p_cback; +} tAVDT_CCB_API_DISCONNECT; + +/* union associated with ccb state machine events */ +typedef union { + tAVDT_CCB_API_DISCOVER discover; + tAVDT_CCB_API_GETCAP getcap; + tAVDT_CCB_API_CONNECT connect; + tAVDT_CCB_API_DISCONNECT disconnect; + tAVDT_MSG msg; + BOOLEAN llcong; + UINT8 err_code; +} tAVDT_CCB_EVT; + +/* channel control block type */ +typedef struct { + BD_ADDR peer_addr; /* BD address of peer */ + TIMER_LIST_ENT timer_entry; /* CCB timer list entry */ + BUFFER_Q cmd_q; /* Queue for outgoing command messages */ + BUFFER_Q rsp_q; /* Queue for outgoing response and reject messages */ + tAVDT_CTRL_CBACK *proc_cback; /* Procedure callback function */ + tAVDT_CTRL_CBACK *p_conn_cback; /* Connection/disconnection callback function */ + void *p_proc_data; /* Pointer to data storage for procedure */ + BT_HDR *p_curr_cmd; /* Current command being sent awaiting response */ + BT_HDR *p_curr_msg; /* Current message being sent */ + BT_HDR *p_rx_msg; /* Current message being received */ + BOOLEAN allocated; /* Whether ccb is allocated */ + UINT8 state; /* The CCB state machine state */ + BOOLEAN ll_opened; /* TRUE if LL is opened */ + BOOLEAN proc_busy; /* TRUE when a discover or get capabilities procedure in progress */ + UINT8 proc_param; /* Procedure parameter; either SEID for get capabilities or number of SEPS for discover */ + BOOLEAN cong; /* Whether signaling channel is congested */ + UINT8 label; /* Message header "label" (sequence number) */ + BOOLEAN reconn; /* If TRUE, reinitiate connection after transitioning from CLOSING to IDLE state */ + UINT8 ret_count; /* Command retransmission count */ +} tAVDT_CCB; + +/* type for action functions */ +typedef void (*tAVDT_CCB_ACTION)(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); + +/* type for AVDT_SCB_API_WRITE_REQ_EVT */ +typedef struct { + BT_HDR *p_buf; + UINT32 time_stamp; +#if AVDT_MULTIPLEXING == TRUE + BUFFER_Q frag_q; /* Queue for outgoing media fragments. p_buf should be 0 */ + UINT8 *p_data; + UINT32 data_len; +#endif + UINT8 m_pt; +} tAVDT_SCB_APIWRITE; + +/* type for AVDT_SCB_TC_CLOSE_EVT */ +typedef struct { + UINT8 old_tc_state; /* channel state before closed */ + UINT8 tcid; /* TCID */ + UINT8 type; /* channel type */ +} tAVDT_SCB_TC_CLOSE; + +/* type for scb event data */ +typedef union { + tAVDT_MSG msg; + tAVDT_SCB_APIWRITE apiwrite; + tAVDT_DELAY_RPT apidelay; + tAVDT_OPEN open; + tAVDT_SCB_TC_CLOSE close; + BOOLEAN llcong; + BT_HDR *p_pkt; +} tAVDT_SCB_EVT; + +/* stream control block type */ +typedef struct { + tAVDT_CS cs; /* stream creation struct */ + tAVDT_CFG curr_cfg; /* current configuration */ + tAVDT_CFG req_cfg; /* requested configuration */ + TIMER_LIST_ENT timer_entry; /* timer entry */ + BT_HDR *p_pkt; /* packet waiting to be sent */ + tAVDT_CCB *p_ccb; /* ccb associated with this scb */ + UINT16 media_seq; /* media packet sequence number */ + BOOLEAN allocated; /* whether scb is allocated or unused */ + BOOLEAN in_use; /* whether stream being used by peer */ + UINT8 role; /* initiator/acceptor role in current procedure */ + BOOLEAN remove; /* whether CB is marked for removal */ + UINT8 state; /* state machine state */ + UINT8 peer_seid; /* SEID of peer stream */ + UINT8 curr_evt; /* current event; set only by state machine */ + BOOLEAN cong; /* Whether media transport channel is congested */ + UINT8 close_code; /* Error code received in close response */ +#if AVDT_MULTIPLEXING == TRUE + BUFFER_Q frag_q; /* Queue for outgoing media fragments */ + UINT32 frag_off; /* length of already received media fragments */ + UINT32 frag_org_len; /* original length before fragmentation of receiving media packet */ + UINT8 *p_next_frag; /* next fragment to send */ + UINT8 *p_media_buf; /* buffer for media packet assigned by AVDT_SetMediaBuf */ + UINT32 media_buf_len; /* length of buffer for media packet assigned by AVDT_SetMediaBuf */ +#endif +} tAVDT_SCB; + +/* type for action functions */ +typedef void (*tAVDT_SCB_ACTION)(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); + +/* adaption layer type for transport channel table */ +typedef struct { + UINT16 peer_mtu; /* L2CAP mtu of the peer device */ + UINT16 my_mtu; /* Our MTU for this channel */ + UINT16 my_flush_to; /* Our flush timeout for this channel */ + UINT16 lcid; + UINT8 tcid; /* transport channel id */ + UINT8 ccb_idx; /* channel control block associated with this tc */ + UINT8 state; /* transport channel state */ + UINT8 cfg_flags; /* L2CAP configuration flags */ + UINT8 id; +} tAVDT_TC_TBL; + +/* adaption layer type for stream routing table */ +typedef struct { + UINT16 lcid; /* L2CAP LCID of the associated transport channel */ + UINT8 scb_hdl; /* stream control block associated with this tc */ +} tAVDT_RT_TBL; + + +/* adaption layer control block */ +typedef struct { + tAVDT_RT_TBL rt_tbl[AVDT_NUM_LINKS][AVDT_NUM_RT_TBL]; + tAVDT_TC_TBL tc_tbl[AVDT_NUM_TC_TBL]; + UINT8 lcid_tbl[MAX_L2CAP_CHANNELS]; /* map LCID to tc_tbl index */ +} tAVDT_AD; + +/* Control block for AVDT */ +typedef struct { + tAVDT_REG rcb; /* registration control block */ + tAVDT_CCB ccb[AVDT_NUM_LINKS]; /* channel control blocks */ + tAVDT_SCB scb[AVDT_NUM_SEPS]; /* stream control blocks */ + tAVDT_AD ad; /* adaption layer control block */ + tAVDTC_CTRL_CBACK *p_conf_cback; /* conformance callback function */ + tAVDT_CCB_ACTION *p_ccb_act; /* pointer to CCB action functions */ + tAVDT_SCB_ACTION *p_scb_act; /* pointer to SCB action functions */ + tAVDT_CTRL_CBACK *p_conn_cback; /* connection callback function */ + UINT8 trace_level; /* trace level */ +} tAVDT_CB; + + +/***************************************************************************** +** function declarations +*****************************************************************************/ + +/* CCB function declarations */ +extern void avdt_ccb_init(void); +extern void avdt_ccb_event(tAVDT_CCB *p_ccb, UINT8 event, tAVDT_CCB_EVT *p_data); +extern tAVDT_CCB *avdt_ccb_by_bd(BD_ADDR bd_addr); +extern tAVDT_CCB *avdt_ccb_alloc(BD_ADDR bd_addr); +extern void avdt_ccb_dealloc(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern UINT8 avdt_ccb_to_idx(tAVDT_CCB *p_ccb); +extern tAVDT_CCB *avdt_ccb_by_idx(UINT8 idx); + +/* CCB action functions */ +extern void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chan_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_close(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_hdl_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_discover_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_discover_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_getcap_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_getcap_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_start_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_start_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_suspend_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_suspend_rsp(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_clear_cmds(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_cmd_fail(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_free_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_cong_state(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ret_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_cmd(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_snd_msg(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_clr_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_reconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_chk_timer(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_set_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_do_disconn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ll_closed(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); +extern void avdt_ccb_ll_opened(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data); + +/* SCB function prototypes */ +extern void avdt_scb_event(tAVDT_SCB *p_scb, UINT8 event, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_init(void); +extern tAVDT_SCB *avdt_scb_alloc(tAVDT_CS *p_cs); +extern void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern UINT8 avdt_scb_to_hdl(tAVDT_SCB *p_scb); +extern tAVDT_SCB *avdt_scb_by_hdl(UINT8 hdl); +extern UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code); +extern void avdt_scb_peer_seid_list(tAVDT_MULTI *p_multi); +extern UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb); + +/* SCB action functions */ +extern void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_delay_rpt_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_delay_rpt_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_delay_rpt_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data); +extern void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, BUFFER_Q *pq); + +/* msg function declarations */ +extern BOOLEAN avdt_msg_send(tAVDT_CCB *p_ccb, BT_HDR *p_msg); +extern void avdt_msg_send_cmd(tAVDT_CCB *p_ccb, void *p_scb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_rsp(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_rej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_send_grej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params); +extern void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf); + +/* adaption layer function declarations */ +extern void avdt_ad_init(void); +extern UINT8 avdt_ad_type_to_tcid(UINT8 type, tAVDT_SCB *p_scb); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_st(UINT8 type, tAVDT_CCB *p_ccb, UINT8 state); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_lcid(UINT16 lcid); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_alloc(tAVDT_CCB *p_ccb); +extern UINT8 avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL *p_tbl); +extern void avdt_ad_tc_close_ind(tAVDT_TC_TBL *p_tbl, UINT16 reason); +extern void avdt_ad_tc_open_ind(tAVDT_TC_TBL *p_tbl); +extern void avdt_ad_tc_cong_ind(tAVDT_TC_TBL *p_tbl, BOOLEAN is_congested); +extern void avdt_ad_tc_data_ind(tAVDT_TC_TBL *p_tbl, BT_HDR *p_buf); +extern tAVDT_TC_TBL *avdt_ad_tc_tbl_by_type(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb); +extern UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf); +extern void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role); +extern void avdt_ad_close_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb); + +extern void avdt_process_timeout(TIMER_LIST_ENT *p_tle); + +/***************************************************************************** +** macros +*****************************************************************************/ + +/* we store the scb and the label in the layer_specific field of the +** current cmd +*/ +#define AVDT_BLD_LAYERSPEC(ls, msg, label) \ + ls = (((label) << 4) | (msg)) + +#define AVDT_LAYERSPEC_LABEL(ls) ((UINT8)((ls) >> 4)) + +#define AVDT_LAYERSPEC_MSG(ls) ((UINT8)((ls) & 0x000F)) + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if AVDT_DYNAMIC_MEMORY == FALSE +AVDT_API extern tAVDT_CB avdt_cb; +#else +AVDT_API extern tAVDT_CB *avdt_cb_ptr; +#define avdt_cb (*avdt_cb_ptr) +#endif + + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO avdt_l2c_appl; + +/* reject message event lookup table */ +extern const UINT8 avdt_msg_rej_2_evt[]; +#ifdef __cplusplus +} +#endif + +#endif /* AVDT_INT_H */ diff --git a/stack/avdt/avdt_l2c.c b/stack/avdt/avdt_l2c.c new file mode 100644 index 0000000..20979b4 --- /dev/null +++ b/stack/avdt/avdt_l2c.c @@ -0,0 +1,521 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 AVDTP adaption layer module interfaces to L2CAP + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "btm_api.h" +#include "btm_int.h" + + +/* callback function declarations */ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avdt_l2c_appl = { + avdt_l2c_connect_ind_cback, + avdt_l2c_connect_cfm_cback, + NULL, + avdt_l2c_config_ind_cback, + avdt_l2c_config_cfm_cback, + avdt_l2c_disconnect_ind_cback, + avdt_l2c_disconnect_cfm_cback, + NULL, + avdt_l2c_data_ind_cback, + avdt_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avdt_sec_check_complete_term +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG1("avdt_sec_check_complete_term res: %d", res); + if (!bd_addr) + { + AVDT_TRACE_WARNING0("avdt_sec_check_complete_term: NULL BD_ADDR"); + return; + + } + p_ccb = avdt_ccb_by_bd(bd_addr); + + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_ACP); + if (p_tbl == NULL) + return; + + if (res == BTM_SUCCESS) + { + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[p_tbl->lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = p_tbl->lcid; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} + +/******************************************************************************* +** +** Function avdt_sec_check_complete_orig +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG1("avdt_sec_check_complete_orig res: %d", res); + if (bd_addr) + p_ccb = avdt_ccb_by_bd(bd_addr); + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_INT); + if(p_tbl == NULL) + return; + + if( res == BTM_SUCCESS ) + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_DisconnectReq (p_tbl->lcid); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} +/******************************************************************************* +** +** Function avdt_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVDT_CCB *p_ccb; + tAVDT_TC_TBL *p_tbl = NULL; + UINT16 result; + tL2CAP_CFG_INFO cfg; + tBTM_STATUS rc; + + /* do we already have a control channel for this peer? */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + /* no, allocate ccb */ + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* allocate and set up entry; first channel is always signaling */ + p_tbl = avdt_ad_tc_tbl_alloc(p_ccb); + p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; + p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; + p_tbl->tcid = AVDT_CHAN_SIG; + p_tbl->lcid = lcid; + p_tbl->id = id; + p_tbl->state = AVDT_AD_ST_SEC_ACP; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_ACP; + + /* Check the security */ + rc = btm_sec_mx_access_request (bd_addr, AVDT_PSM, + FALSE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_term, NULL); + if(rc == BTM_CMD_STARTED) + { + L2CA_ConnectRsp (p_ccb->peer_addr, p_tbl->id, lcid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + } + /* deal with simultaneous control channel connect case */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_CONN)) != NULL) + { + /* reject their connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + /* this must be a traffic channel; are we accepting a traffic channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_MEDIA, p_ccb, AVDT_AD_ST_ACP)) != NULL) + { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#if AVDT_REPORTING == TRUE + /* this must be a reporting channel; are we accepting a reporting channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_REPORT, p_ccb, AVDT_AD_ST_ACP)) != NULL) + { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#endif + /* else we're not listening for traffic channel; reject */ + else + { + result = L2CAP_CONN_NO_PSM; + } + + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl); + avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + tL2CAP_CFG_INFO cfg; + tAVDT_CCB *p_ccb; + + AVDT_TRACE_DEBUG2("avdt_l2c_connect_cfm_cback lcid: %d, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + if(p_tbl->tcid != AVDT_CHAN_SIG) + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } + else + { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + if(p_ccb == NULL) + { + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_SEC_INT; + p_tbl->lcid = lcid; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_INT; + + /* Check the security */ + btm_sec_mx_access_request (p_ccb->peer_addr, AVDT_PSM, + TRUE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_orig, NULL); + } + } + } + + /* failure; notify adaption that channel closed */ + if (result != L2CAP_CONN_OK) + { + avdt_ad_tc_close_ind(p_tbl, result); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CONN_OK) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) + { + avdt_ad_tc_open_ind(p_tbl); + } + } + /* else failure */ + else + { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_tbl->peer_mtu = p_cfg->mtu; + } + else + { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } + AVDT_TRACE_DEBUG2("peer_mtu: %d, lcid: x%x",p_tbl->peer_mtu, lcid); + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) == 0) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_CFM_DONE) + { + avdt_ad_tc_open_ind(p_tbl); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG2("avdt_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d", + lcid, ack_needed); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avdt_ad_tc_close_ind(p_tbl, 0); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG2("avdt_l2c_disconnect_cfm_cback lcid: %d, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_close_ind(p_tbl, result); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_cong_ind(p_tbl, is_congested); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_data_ind(p_tbl, p_buf); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + diff --git a/stack/avdt/avdt_msg.c b/stack/avdt/avdt_msg.c new file mode 100644 index 0000000..5d9c2fb --- /dev/null +++ b/stack/avdt/avdt_msg.c @@ -0,0 +1,1891 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains functions for parsing and building AVDTP signaling + * messages. It also contains functions called by the SCB or CCB state + * machines for sending command, response, and reject messages. It also + * contains a function that processes incoming messages and dispatches them + * to the appropriate SCB or CCB. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "gki.h" +#include "btu.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* mask of all psc values */ +#define AVDT_MSG_PSC_MASK (AVDT_PSC_TRANS | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT | \ + AVDT_PSC_RECOV | AVDT_PSC_HDRCMP | AVDT_PSC_MUX) +#define AVDT_PSC_PROTECT (1<<4) /* Content Protection */ +#define AVDT_PSC_CODEC (1<<7) /* codec */ + + +/***************************************************************************** +** type definitions +*****************************************************************************/ + +/* type for message building functions */ +typedef void (*tAVDT_MSG_BLD)(UINT8 **p, tAVDT_MSG *p_msg); + +/* type for message parsing functions */ +typedef UINT8 (*tAVDT_MSG_PRS)(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); + + +/***************************************************************************** +** local function declarations +*****************************************************************************/ + +static void avdt_msg_bld_none(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_single(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_setconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_reconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_multi(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_security_cmd(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_discover_rsp(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_svccap(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_security_rsp(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_all_svccap(UINT8 **p, tAVDT_MSG *p_msg); +static void avdt_msg_bld_delay_rpt(UINT8 **p, tAVDT_MSG *p_msg); + +static UINT8 avdt_msg_prs_none(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_single(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_setconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_reconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_multi(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_security_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_discover_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_all_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_security_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); +static UINT8 avdt_msg_prs_delay_rpt (tAVDT_MSG *p_msg, UINT8 *p, UINT16 len); + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* table of information element minimum lengths used for parsing */ +const UINT8 avdt_msg_ie_len_min[] = { + 0, /* unused */ + AVDT_LEN_TRANS_MIN, /* media transport */ + AVDT_LEN_REPORT_MIN, /* reporting */ + AVDT_LEN_RECOV_MIN, /* recovery */ + AVDT_LEN_PROTECT_MIN, /* content protection */ + AVDT_LEN_HDRCMP_MIN, /* header compression */ + AVDT_LEN_MUX_MIN, /* multiplexing */ + AVDT_LEN_CODEC_MIN, /* codec */ + AVDT_LEN_DELAY_RPT_MIN /* delay report */ +}; + +/* table of information element minimum lengths used for parsing */ +const UINT8 avdt_msg_ie_len_max[] = { + 0, /* unused */ + AVDT_LEN_TRANS_MAX, /* media transport */ + AVDT_LEN_REPORT_MAX, /* reporting */ + AVDT_LEN_RECOV_MAX, /* recovery */ + AVDT_LEN_PROTECT_MAX, /* content protection */ + AVDT_LEN_HDRCMP_MAX, /* header compression */ + AVDT_LEN_MUX_MAX, /* multiplexing */ + AVDT_LEN_CODEC_MAX, /* codec */ + AVDT_LEN_DELAY_RPT_MAX /* delay report */ +}; + +/* table of error codes used when decoding information elements */ +const UINT8 avdt_msg_ie_err[] = { + 0, /* unused */ + AVDT_ERR_MEDIA_TRANS, /* media transport */ + AVDT_ERR_LENGTH, /* reporting */ + AVDT_ERR_RECOV_FMT, /* recovery */ + AVDT_ERR_CP_FMT, /* content protection */ + AVDT_ERR_ROHC_FMT, /* header compression */ + AVDT_ERR_MUX_FMT, /* multiplexing */ + AVDT_ERR_SERVICE, /* codec */ + AVDT_ERR_SERVICE /* delay report ?? */ +}; + +/* table of packet type minimum lengths */ +static const UINT8 avdt_msg_pkt_type_len[] = { + AVDT_LEN_TYPE_SINGLE, + AVDT_LEN_TYPE_START, + AVDT_LEN_TYPE_CONT, + AVDT_LEN_TYPE_END +}; + +/* function table for building command messages */ +const tAVDT_MSG_BLD avdt_msg_bld_cmd[] = { + avdt_msg_bld_none, /* discover */ + avdt_msg_bld_single, /* get capabilities */ + avdt_msg_bld_setconfig_cmd, /* set configuration */ + avdt_msg_bld_single, /* get configuration */ + avdt_msg_bld_reconfig_cmd, /* reconfigure */ + avdt_msg_bld_single, /* open */ + avdt_msg_bld_multi, /* start */ + avdt_msg_bld_single, /* close */ + avdt_msg_bld_multi, /* suspend */ + avdt_msg_bld_single, /* abort */ + avdt_msg_bld_security_cmd, /* security control */ + avdt_msg_bld_single, /* get all capabilities */ + avdt_msg_bld_delay_rpt /* delay report */ +}; + +/* function table for building response messages */ +const tAVDT_MSG_BLD avdt_msg_bld_rsp[] = { + avdt_msg_bld_discover_rsp, /* discover */ + avdt_msg_bld_svccap, /* get capabilities */ + avdt_msg_bld_none, /* set configuration */ + avdt_msg_bld_all_svccap, /* get configuration */ + avdt_msg_bld_none, /* reconfigure */ + avdt_msg_bld_none, /* open */ + avdt_msg_bld_none, /* start */ + avdt_msg_bld_none, /* close */ + avdt_msg_bld_none, /* suspend */ + avdt_msg_bld_none, /* abort */ + avdt_msg_bld_security_rsp, /* security control */ + avdt_msg_bld_all_svccap, /* get all capabilities */ + avdt_msg_bld_none /* delay report */ +}; + +/* function table for parsing command messages */ +const tAVDT_MSG_PRS avdt_msg_prs_cmd[] = { + avdt_msg_prs_none, /* discover */ + avdt_msg_prs_single, /* get capabilities */ + avdt_msg_prs_setconfig_cmd, /* set configuration */ + avdt_msg_prs_single, /* get configuration */ + avdt_msg_prs_reconfig_cmd, /* reconfigure */ + avdt_msg_prs_single, /* open */ + avdt_msg_prs_multi, /* start */ + avdt_msg_prs_single, /* close */ + avdt_msg_prs_multi, /* suspend */ + avdt_msg_prs_single, /* abort */ + avdt_msg_prs_security_cmd, /* security control */ + avdt_msg_prs_single, /* get all capabilities */ + avdt_msg_prs_delay_rpt /* delay report */ +}; + +/* function table for parsing response messages */ +const tAVDT_MSG_PRS avdt_msg_prs_rsp[] = { + avdt_msg_prs_discover_rsp, /* discover */ + avdt_msg_prs_svccap, /* get capabilities */ + avdt_msg_prs_none, /* set configuration */ + avdt_msg_prs_all_svccap, /* get configuration */ + avdt_msg_prs_none, /* reconfigure */ + avdt_msg_prs_none, /* open */ + avdt_msg_prs_none, /* start */ + avdt_msg_prs_none, /* close */ + avdt_msg_prs_none, /* suspend */ + avdt_msg_prs_none, /* abort */ + avdt_msg_prs_security_rsp, /* security control */ + avdt_msg_prs_all_svccap, /* get all capabilities */ + avdt_msg_prs_none /* delay report */ +}; + +/* command message-to-event lookup table */ +const UINT8 avdt_msg_cmd_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_CMD_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_CMD_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_CMD_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_CMD_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_CMD_EVT, /* open */ + AVDT_CCB_MSG_START_CMD_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_CMD_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_CMD_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_CMD_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_CMD_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get all capabilities */ + AVDT_SCB_MSG_DELAY_RPT_CMD_EVT /* delay report */ +}; + +/* response message-to-event lookup table */ +const UINT8 avdt_msg_rsp_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_RSP_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_RSP_EVT, /* open */ + AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ + AVDT_SCB_MSG_DELAY_RPT_RSP_EVT /* delay report */ +}; + +/* reject message-to-event lookup table */ +const UINT8 avdt_msg_rej_2_evt[] = { + AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ + AVDT_SCB_MSG_SETCONFIG_REJ_EVT, /* set configuration */ + AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ + AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ + AVDT_SCB_MSG_OPEN_REJ_EVT, /* open */ + AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ + AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ + AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ + AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ + AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ + AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ + 0 /* delay report */ +}; + +/******************************************************************************* +** +** Function avdt_msg_bld_cfg +** +** Description This function builds the configuration parameters contained +** in a command or response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_cfg(UINT8 **p, tAVDT_CFG *p_cfg) +{ + UINT8 len; + + /* for now, just build media transport, codec, and content protection, and multiplexing */ + + /* media transport */ + if (p_cfg->psc_mask & AVDT_PSC_TRANS) + { + *(*p)++ = AVDT_CAT_TRANS; + *(*p)++ = 0; /* length */ + } + +#if AVDT_REPORTING == TRUE + /* reporting transport */ + if (p_cfg->psc_mask & AVDT_PSC_REPORT) + { + *(*p)++ = AVDT_CAT_REPORT; + *(*p)++ = 0; /* length */ + } +#endif + + /* codec */ + if (p_cfg->num_codec != 0) + { + *(*p)++ = AVDT_CAT_CODEC; + len = p_cfg->codec_info[0] + 1; + if( len > AVDT_CODEC_SIZE ) + len = AVDT_CODEC_SIZE; + + memcpy(*p, p_cfg->codec_info, len); + *p += len; + } + + /* content protection */ + if (p_cfg->num_protect != 0) + { + *(*p)++ = AVDT_CAT_PROTECT; + len = p_cfg->protect_info[0] + 1; + if( len > AVDT_PROTECT_SIZE ) + len = AVDT_PROTECT_SIZE; + + memcpy(*p, p_cfg->protect_info, len); + *p += len; + } + +#if AVDT_MULTIPLEXING == TRUE + /* multiplexing */ + if (p_cfg->psc_mask & AVDT_PSC_MUX) + { + *(*p)++ = AVDT_CAT_MUX; + /* length */ + if (p_cfg->psc_mask & AVDT_PSC_RECOV) + *(*p)++ = 7; /* frag (1) + media + report + recovery */ + else if (p_cfg->psc_mask & AVDT_PSC_REPORT) + *(*p)++ = 5; /* frag (1) + media + report */ + else + *(*p)++ = 3; /* frag (1) + media */ + + /* allow fragmentation */ + if(p_cfg->mux_mask & AVDT_MUX_FRAG) + *(*p)++ = 0x80; + else + *(*p)++ = 0; + + /* media transport session */ + *(*p)++ = p_cfg->mux_tsid_media<<3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_media<<3; /* TCID */ + + if (p_cfg->psc_mask & AVDT_PSC_RECOV) + { + /* reporting transport session */ + *(*p)++ = p_cfg->mux_tsid_report<<3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_report<<3; /* TCID */ + /* recovery transport session */ + *(*p)++ = p_cfg->mux_tsid_recov<<3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_recov<<3; /* TCID */ + } + else if (p_cfg->psc_mask & AVDT_PSC_REPORT) + { + /* reporting transport session */ + *(*p)++ = p_cfg->mux_tsid_report<<3; /* TSID */ + *(*p)++ = p_cfg->mux_tcid_report<<3; /* TCID */ + } + } +#endif + + /* delay report */ + if (p_cfg->psc_mask & AVDT_PSC_DELAY_RPT) + { + *(*p)++ = AVDT_CAT_DELAY_RPT; + *(*p)++ = 0; /* length */ + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_none +** +** Description This message building function builds an empty message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_none(UINT8 **p, tAVDT_MSG *p_msg) +{ + return; +} + +/******************************************************************************* +** +** Function avdt_msg_bld_single +** +** Description This message building function builds a message containing +** a single SEID. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_single(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->single.seid); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_setconfig_cmd +** +** Description This message building function builds a set configuration +** command message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_setconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.hdr.seid); + AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.int_seid); + avdt_msg_bld_cfg(p, p_msg->config_cmd.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_reconfig_cmd +** +** Description This message building function builds a reconfiguration +** command message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_reconfig_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->reconfig_cmd.hdr.seid); + + /* force psc mask zero to build only codec and security */ + p_msg->reconfig_cmd.p_cfg->psc_mask = 0; + avdt_msg_bld_cfg(p, p_msg->reconfig_cmd.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_multi +** +** Description This message building function builds a message containing +** multiple SEID's. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_multi(UINT8 **p, tAVDT_MSG *p_msg) +{ + int i; + + for (i = 0; i < p_msg->multi.num_seps; i++) + { + AVDT_MSG_BLD_SEID(*p, p_msg->multi.seid_list[i]); + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_security_cmd +** +** Description This message building function builds a security +** command message. +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_security_cmd(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->security_cmd.hdr.seid); + memcpy(*p, p_msg->security_cmd.p_data, p_msg->security_cmd.len); + *p += p_msg->security_cmd.len; +} + +/******************************************************************************* +** +** Function avdt_msg_bld_delay_rpt +** +** Description This message building function builds a delay report +** command message. +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_delay_rpt(UINT8 **p, tAVDT_MSG *p_msg) +{ + AVDT_MSG_BLD_SEID(*p, p_msg->delay_rpt_cmd.hdr.seid); + UINT16_TO_BE_STREAM(*p, p_msg->delay_rpt_cmd.delay); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_discover_rsp +** +** Description This message building function builds a discover +** response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_discover_rsp(UINT8 **p, tAVDT_MSG *p_msg) +{ + int i; + + for (i = 0; i < p_msg->discover_rsp.num_seps; i++) + { + /* build discover rsp info */ + AVDT_MSG_BLD_DISC(*p, p_msg->discover_rsp.p_sep_info[i].seid, + p_msg->discover_rsp.p_sep_info[i].in_use, + p_msg->discover_rsp.p_sep_info[i].media_type, + p_msg->discover_rsp.p_sep_info[i].tsep); + } +} + +/******************************************************************************* +** +** Function avdt_msg_bld_svccap +** +** Description This message building function builds a message containing +** service capabilities parameters. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_svccap(UINT8 **p, tAVDT_MSG *p_msg) +{ + tAVDT_CFG cfg; + + /* make sure the delay report category is not reported */ + memcpy (&cfg, p_msg->svccap.p_cfg, sizeof(tAVDT_CFG)); + cfg.psc_mask &= ~AVDT_PSC_DELAY_RPT; + avdt_msg_bld_cfg(p, &cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_all_svccap +** +** Description This message building function builds a message containing +** service capabilities parameters. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_all_svccap(UINT8 **p, tAVDT_MSG *p_msg) +{ + avdt_msg_bld_cfg(p, p_msg->svccap.p_cfg); +} + +/******************************************************************************* +** +** Function avdt_msg_bld_security_rsp +** +** Description This message building function builds a security +** response message. +** +** +** Returns void. +** +*******************************************************************************/ +static void avdt_msg_bld_security_rsp(UINT8 **p, tAVDT_MSG *p_msg) +{ + memcpy(*p, p_msg->security_rsp.p_data, p_msg->security_rsp.len); + *p += p_msg->security_rsp.len; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_cfg +** +** Description This message parsing function parses the configuration +** parameters field of a message. +** +** +** Returns Error code or zero if no error, and element that failed +** in p_elem. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_cfg(tAVDT_CFG *p_cfg, UINT8 *p, UINT16 len, UINT8* p_elem, UINT8 sig_id) +{ + UINT8 *p_end; + UINT8 elem = 0; + UINT8 elem_len; + UINT8 tmp; + UINT8 err = 0; + UINT8 protect_offset = 0; + + if (!p_cfg) + { + AVDT_TRACE_ERROR0 ("not expecting this cfg"); + return AVDT_ERR_BAD_STATE; + } + + p_cfg->psc_mask = 0; + p_cfg->num_codec = 0; + p_cfg->num_protect = 0; +#if AVDT_MULTIPLEXING == TRUE + p_cfg->mux_mask = 0; +#endif + + /* while there is still data to parse */ + p_end = p + len; + while ((p < p_end) && (err == 0)) + { + /* verify overall length */ + if ((p_end - p) < AVDT_LEN_CFG_MIN) + { + err = AVDT_ERR_PAYLOAD; + break; + } + + /* get and verify info elem id, length */ + elem = *p++; + elem_len = *p++; + + if ((elem == 0) || (elem > AVDT_CAT_MAX_CUR)) + { + /* this may not be really bad. + * It may be a service category that is too new for us. + * allow these to be parsed without reporting an error. + * If this is a "capability" (as in GetCapRsp & GetConfigRsp), this is filtered out. + * If this is a Configuration (as in SetConfigCmd & ReconfigCmd), + * this will be marked as an error in the caller of this function */ + if ((sig_id == AVDT_SIG_SETCONFIG) || (sig_id == AVDT_SIG_RECONFIG)) + { + /* Cannot accept unknown category. */ + err = AVDT_ERR_CATEGORY; + break; + } + else /* GETCAP or GET_ALLCAP */ + { + /* Skip unknown categories. */ + p += elem_len; + AVDT_TRACE_DEBUG2("skipping unknown service category=%d len: %d", elem, elem_len); + continue; + } + } + + if ((elem_len > avdt_msg_ie_len_max[elem]) || + (elem_len < avdt_msg_ie_len_min[elem])) + { + err = avdt_msg_ie_err[elem]; + break; + } + + /* add element to psc mask, but mask out codec or protect */ + p_cfg->psc_mask |= (1 << elem); + AVDT_TRACE_DEBUG3("elem=%d elem_len: %d psc_mask=0x%x", elem, elem_len, p_cfg->psc_mask); + + /* parse individual information elements with additional parameters */ + switch (elem) + { + case AVDT_CAT_RECOV: + p_cfg->recov_type = *p++; + p_cfg->recov_mrws = *p++; + p_cfg->recov_mnmp = *p++; + if (p_cfg->recov_type != AVDT_RECOV_RFC2733) + { + err = AVDT_ERR_RECOV_TYPE; + } + else if ((p_cfg->recov_mrws < AVDT_RECOV_MRWS_MIN) || + (p_cfg->recov_mrws > AVDT_RECOV_MRWS_MAX) || + (p_cfg->recov_mnmp < AVDT_RECOV_MNMP_MIN) || + (p_cfg->recov_mnmp > AVDT_RECOV_MNMP_MAX)) + { + err = AVDT_ERR_RECOV_FMT; + } + break; + + case AVDT_CAT_PROTECT: + p_cfg->psc_mask &= ~AVDT_PSC_PROTECT; + if ((elem_len + protect_offset) < AVDT_PROTECT_SIZE) + { + p_cfg->num_protect++; + p_cfg->protect_info[protect_offset] = elem_len; + protect_offset++; + memcpy(&p_cfg->protect_info[protect_offset], p, elem_len); + protect_offset += elem_len; + } + p += elem_len; + break; + + case AVDT_CAT_HDRCMP: + p_cfg->hdrcmp_mask = *p++; + break; + +#if AVDT_MULTIPLEXING == TRUE + case AVDT_CAT_MUX: + /* verify length */ + AVDT_TRACE_WARNING2("psc_mask=0x%x elem_len=%d", p_cfg->psc_mask, elem_len); + if( ((0 == (p_cfg->psc_mask & (AVDT_PSC_RECOV|AVDT_PSC_REPORT))) && (elem_len != 3)) + || ((p_cfg->psc_mask & AVDT_PSC_RECOV) && (elem_len != 7)) + || ((p_cfg->psc_mask & AVDT_PSC_REPORT) && (elem_len != 5)) ) + { + err = AVDT_ERR_MUX_FMT; + break; + } + + /* parse fragmentation */ + p_cfg->mux_mask = *p++ & (UINT8)AVDT_MUX_FRAG; + + /* parse TSIDs and TCIDs */ + if(--elem_len) + p_cfg->mux_tsid_media = (*p++)>>3; + else + break; + + if(--elem_len) + p_cfg->mux_tcid_media = (*p++)>>3; + else + break; + + if(--elem_len) + p_cfg->mux_tsid_report = (*p++)>>3; + else + break; + + if(--elem_len) + p_cfg->mux_tcid_report = (*p++)>>3; + else + break; + + if(--elem_len) + p_cfg->mux_tsid_recov = (*p++)>>3; + else + break; + + if(--elem_len) + p_cfg->mux_tcid_recov = (*p++)>>3; + else + break; + break; +#endif + + case AVDT_CAT_CODEC: + p_cfg->psc_mask &= ~AVDT_PSC_CODEC; + tmp = elem_len; + if (elem_len >= AVDT_CODEC_SIZE) + { + tmp = AVDT_CODEC_SIZE - 1; + } + p_cfg->num_codec++; + p_cfg->codec_info[0] = elem_len; + memcpy(&p_cfg->codec_info[1], p, tmp); + p += elem_len; + break; + + case AVDT_CAT_DELAY_RPT: + break; + + default: + p += elem_len; + break; + } /* switch */ + } /* while ! err, !end*/ + *p_elem = elem; + AVDT_TRACE_DEBUG3("err=0x%x, elem:0x%x psc_mask=0x%x", err, elem, p_cfg->psc_mask); + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_none +** +** Description This message parsing function parses a message with no parameters. + +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_none(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_single +** +** Description This message parsing function parses a message with a +** single SEID. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_single(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len != AVDT_LEN_SINGLE) + { + err = AVDT_ERR_LENGTH; + } + else + { + AVDT_MSG_PRS_SEID(p, p_msg->single.seid); + + if (avdt_scb_by_hdl(p_msg->single.seid) == NULL) + { + err = AVDT_ERR_SEID; + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_setconfig_cmd +** +** Description This message parsing function parses a set configuration +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_setconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_SETCONFIG_MIN) + { + err = AVDT_ERR_LENGTH; + } + else + { + /* get seids */ + AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->config_cmd.hdr.seid) == NULL) + { + err = AVDT_ERR_SEID; + } + + AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.int_seid); + if ((p_msg->config_cmd.int_seid < AVDT_SEID_MIN) || + (p_msg->config_cmd.int_seid > AVDT_SEID_MAX)) + { + err = AVDT_ERR_SEID; + } + } + + if (!err) + { + /* parse configuration parameters */ + len -= 2; + err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_SETCONFIG); + + if (!err) + { + /* verify protocol service capabilities are supported */ + if (((p_msg->config_cmd.p_cfg->psc_mask & (~AVDT_PSC)) != 0) || + (p_msg->config_cmd.p_cfg->num_codec == 0)) + { + err = AVDT_ERR_INVALID_CAP; + } + } + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_reconfig_cmd +** +** Description This message parsing function parses a reconfiguration +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_reconfig_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_RECONFIG_MIN) + { + err = AVDT_ERR_LENGTH; + } + else + { + /* get seid */ + AVDT_MSG_PRS_SEID(p, p_msg->reconfig_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->reconfig_cmd.hdr.seid) == NULL) + { + err = AVDT_ERR_SEID; + } + else + { + /* parse config parameters */ + len--; + err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_RECONFIG); + + /* verify no protocol service capabilities in parameters */ + if (!err) + { + AVDT_TRACE_DEBUG2("avdt_msg_prs_reconfig_cmd psc_mask=0x%x/0x%x", p_msg->config_cmd.p_cfg->psc_mask, AVDT_MSG_PSC_MASK); + if ((p_msg->config_cmd.p_cfg->psc_mask != 0) || + (p_msg->config_cmd.p_cfg->num_codec == 0 && p_msg->config_cmd.p_cfg->num_protect == 0)) + { + err = AVDT_ERR_INVALID_CAP; + } + } + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_multi +** +** Description This message parsing function parses a message containing +** multiple SEID's. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_multi(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + int i; + UINT8 err = 0; + + p_msg->hdr.err_param = 0; + + /* verify len */ + if (len < AVDT_LEN_MULTI_MIN || (len > AVDT_NUM_SEPS)) + { + err = AVDT_ERR_LENGTH; + } + else + { + /* get and verify all seps */ + for (i = 0; i < len; i++) + { + AVDT_MSG_PRS_SEID(p, p_msg->multi.seid_list[i]); + if (avdt_scb_by_hdl(p_msg->multi.seid_list[i]) == NULL) + { + err = AVDT_ERR_SEID; + p_msg->hdr.err_param = p_msg->multi.seid_list[i]; + break; + } + } + p_msg->multi.num_seps = (UINT8)i; + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_security_cmd +** +** Description This message parsing function parses a security +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_security_cmd(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len < AVDT_LEN_SECURITY_MIN) + { + err = AVDT_ERR_LENGTH; + } + else + { + /* get seid */ + AVDT_MSG_PRS_SEID(p, p_msg->security_cmd.hdr.seid); + if (avdt_scb_by_hdl(p_msg->security_cmd.hdr.seid) == NULL) + { + err = AVDT_ERR_SEID; + } + else + { + p_msg->security_cmd.p_data = p; + p_msg->security_cmd.len = len - 1; + } + } + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_discover_rsp +** +** Description This message parsing function parses a discover +** response message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_discover_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + int i; + UINT8 err = 0; + + /* determine number of seps; seps in msg is len/2, but set to minimum + ** of seps app has supplied memory for and seps in msg + */ + if (p_msg->discover_rsp.num_seps > (len / 2)) + { + p_msg->discover_rsp.num_seps = (len / 2); + } + + /* parse out sep info */ + for (i = 0; i < p_msg->discover_rsp.num_seps; i++) + { + /* parse discover rsp info */ + AVDT_MSG_PRS_DISC(p, p_msg->discover_rsp.p_sep_info[i].seid, + p_msg->discover_rsp.p_sep_info[i].in_use, + p_msg->discover_rsp.p_sep_info[i].media_type, + p_msg->discover_rsp.p_sep_info[i].tsep); + + /* verify that seid is valid */ + if ((p_msg->discover_rsp.p_sep_info[i].seid < AVDT_SEID_MIN) || + (p_msg->discover_rsp.p_sep_info[i].seid > AVDT_SEID_MAX)) + { + err = AVDT_ERR_SEID; + break; + } + } + + return err; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_svccap +** +** Description This message parsing function parses a message containing +** service capabilities parameters. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + /* parse parameters */ + UINT8 err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_GETCAP); + if (p_msg->svccap.p_cfg) + { + p_msg->svccap.p_cfg->psc_mask &= AVDT_LEG_PSC; + } + + return (err); +} + +/******************************************************************************* +** +** Function avdt_msg_prs_all_svccap +** +** Description This message parsing function parses a message containing +** service capabilities parameters. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_all_svccap(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, &p_msg->hdr.err_param, AVDT_SIG_GET_ALLCAP); + if (p_msg->svccap.p_cfg) + { + p_msg->svccap.p_cfg->psc_mask &= AVDT_MSG_PSC_MASK; + } + return (err); +} + +/******************************************************************************* +** +** Function avdt_msg_prs_security_rsp +** +** Description This message parsing function parsing a security +** response message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_security_rsp(tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + p_msg->security_rsp.p_data = p; + p_msg->security_rsp.len = len; + + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_rej +** +** Description +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_rej(tAVDT_MSG *p_msg, UINT8 *p, UINT8 sig) +{ + if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) + { + p_msg->hdr.err_param = *p++; + p_msg->hdr.err_code = *p; + } + else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) + { + AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param); + p_msg->hdr.err_code = *p; + } + else + { + p_msg->hdr.err_code = *p; + } + + return 0; +} + +/******************************************************************************* +** +** Function avdt_msg_prs_delay_rpt +** +** Description This message parsing function parses a security +** command message. +** +** +** Returns Error code or zero if no error. +** +*******************************************************************************/ +static UINT8 avdt_msg_prs_delay_rpt (tAVDT_MSG *p_msg, UINT8 *p, UINT16 len) +{ + UINT8 err = 0; + + /* verify len */ + if (len != AVDT_LEN_DELAY_RPT) + { + AVDT_TRACE_WARNING2("avdt_msg_prs_delay_rpt expected len: %u got: %u", AVDT_LEN_DELAY_RPT, len); + err = AVDT_ERR_LENGTH; + } + else + { + /* get seid */ + AVDT_MSG_PRS_SEID (p, p_msg->delay_rpt_cmd.hdr.seid); + + if (avdt_scb_by_hdl(p_msg->delay_rpt_cmd.hdr.seid) == NULL) + { + err = AVDT_ERR_SEID; + } + else + { + BE_STREAM_TO_UINT16 (p_msg->delay_rpt_cmd.delay, p); + AVDT_TRACE_DEBUG1("avdt_msg_prs_delay_rpt delay: %u", p_msg->delay_rpt_cmd.delay); + } + } + return err; +} + + +/******************************************************************************* +** +** Function avdt_msg_send +** +** Description Send, and if necessary fragment the next message. +** +** +** Returns Congested state; TRUE if CCB congested, FALSE if not. +** +*******************************************************************************/ +BOOLEAN avdt_msg_send(tAVDT_CCB *p_ccb, BT_HDR *p_msg) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 hdr_len; + tAVDT_TC_TBL *p_tbl; + BT_HDR *p_buf; + UINT8 *p; + UINT8 label; + UINT8 msg; + UINT8 sig; + UINT8 nosp = 0; /* number of subsequent packets */ + + /* look up transport channel table entry to get peer mtu */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_SIG, p_ccb, NULL); + + /* set the current message if there is a message passed in */ + if (p_msg != NULL) + { + p_ccb->p_curr_msg = p_msg; + } + + /* store copy of curr_msg->len */ + curr_msg_len = p_ccb->p_curr_msg->len; + + /* while not congested and we haven't sent it all */ + while ((!p_ccb->cong) && (p_ccb->p_curr_msg != NULL)) + { + /* check what kind of message we've got here; we are using the offset + ** to indicate that a message is being fragmented + */ + + /* if message isn't being fragmented and it fits in mtu */ + if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len <= p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) + { + pkt_type = AVDT_PKT_TYPE_SINGLE; + hdr_len = AVDT_LEN_TYPE_SINGLE; + p_buf = p_ccb->p_curr_msg; + } + /* if message isn't being fragmented and it doesn't fit in mtu */ + else if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len > p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) + { + pkt_type = AVDT_PKT_TYPE_START; + hdr_len = AVDT_LEN_TYPE_START; + nosp = (p_ccb->p_curr_msg->len + AVDT_LEN_TYPE_START - p_tbl->peer_mtu) / + (p_tbl->peer_mtu - 1) + 2; + + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID)) == NULL) + { + /* do we even want to try and recover from this? could do so + by setting retransmission timer */ + return TRUE; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_tbl->peer_mtu - hdr_len; + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, p_buf->len); + } + /* if message is being fragmented and remaining bytes don't fit in mtu */ + else if ((p_ccb->p_curr_msg->offset > AVDT_MSG_OFFSET) && + (p_ccb->p_curr_msg->len > (p_tbl->peer_mtu - AVDT_LEN_TYPE_CONT))) + { + pkt_type = AVDT_PKT_TYPE_CONT; + hdr_len = AVDT_LEN_TYPE_CONT; + + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID)) == NULL) + { + /* do we even want to try and recover from this? could do so + by setting retransmission timer */ + return TRUE; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_tbl->peer_mtu - hdr_len; + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, p_buf->len); + } + /* if message is being fragmented and remaining bytes do fit in mtu */ + else + { + pkt_type = AVDT_PKT_TYPE_END; + hdr_len = AVDT_LEN_TYPE_END; + p_buf = p_ccb->p_curr_msg; + } + + /* label, sig id, msg type are in hdr of p_curr_msg */ + label = AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_msg->layer_specific); + msg = AVDT_LAYERSPEC_MSG(p_ccb->p_curr_msg->layer_specific); + sig = (UINT8) p_ccb->p_curr_msg->event; + AVDT_TRACE_DEBUG3("avdt_msg_send label:%d, msg:%d, sig:%d", label, msg, sig); + + /* keep track of how much of msg we've sent */ + curr_msg_len -= p_buf->len; + if (curr_msg_len == 0) + { + /* entire message sent; mark as finished */ + p_ccb->p_curr_msg = NULL; + + /* start timer here for commands */ + if (msg == AVDT_MSG_TYPE_CMD) + { + /* if retransmit timeout set to zero, sig doesn't use retransmit */ + if ((sig == AVDT_SIG_DISCOVER) || (sig == AVDT_SIG_GETCAP) || + (sig == AVDT_SIG_SECURITY) || (avdt_cb.rcb.ret_tout == 0)) + { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RSP, avdt_cb.rcb.sig_tout); + } + else if (sig != AVDT_SIG_DELAY_RPT) + { + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_AVDT_CCB_RET, avdt_cb.rcb.ret_tout); + } + } + } + else + { + /* message being fragmented and not completely sent */ + p_ccb->p_curr_msg->len -= p_buf->len; + p_ccb->p_curr_msg->offset += p_buf->len; + } + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVDT_MSG_BLD_HDR(p, label, pkt_type, msg); + if (pkt_type == AVDT_PKT_TYPE_START) + { + AVDT_MSG_BLD_NOSP(p, nosp); + } + if ((pkt_type == AVDT_PKT_TYPE_START) || (pkt_type == AVDT_PKT_TYPE_SINGLE)) + { + AVDT_MSG_BLD_SIG(p, sig); + } + + /* send msg buffer down */ + avdt_ad_write_req(AVDT_CHAN_SIG, p_ccb, NULL, p_buf); + } + return (p_ccb->cong); +} + +/******************************************************************************* +** +** Function avdt_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +BT_HDR *avdt_msg_asmbl(tAVDT_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVDT_MSG_PRS_PKT_TYPE(p, pkt_type); + + /* quick sanity check on length */ + if (p_buf->len < avdt_msg_pkt_type_len[pkt_type]) + { + GKI_freebuf(p_buf); + AVDT_TRACE_WARNING0("Bad length during reassembly"); + p_ret = NULL; + } + /* single packet */ + else if (pkt_type == AVDT_PKT_TYPE_SINGLE) + { + /* if reassembly in progress drop message and process new single */ + if (p_ccb->p_rx_msg != NULL) + { + GKI_freebuf(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + AVDT_TRACE_WARNING0("Got single during reassembly"); + } + p_ret = p_buf; + } + /* start packet */ + else if (pkt_type == AVDT_PKT_TYPE_START) + { + /* if reassembly in progress drop message and process new single */ + if (p_ccb->p_rx_msg != NULL) + { + GKI_freebuf(p_ccb->p_rx_msg); + AVDT_TRACE_WARNING0("Got start during reassembly"); + } + p_ccb->p_rx_msg = p_buf; + + /* copy first header byte over nosp */ + *(p + 1) = *p; + + /* set offset to point to where to copy next */ + p_ccb->p_rx_msg->offset += p_ccb->p_rx_msg->len; + + /* adjust length for packet header */ + p_ccb->p_rx_msg->len -= 1; + + p_ret = NULL; + } + /* continue or end */ + else + { + /* if no reassembly in progress drop message */ + if (p_ccb->p_rx_msg == NULL) + { + GKI_freebuf(p_buf); + AVDT_TRACE_WARNING1("Pkt type=%d out of order", pkt_type); + p_ret = NULL; + } + else + { + /* get size of buffer holding assembled message */ + buf_len = GKI_get_buf_size(p_ccb->p_rx_msg) - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVDT_LEN_TYPE_CONT; + p_buf->len -= AVDT_LEN_TYPE_CONT; + + /* verify length */ + if ((p_ccb->p_rx_msg->offset + p_buf->len) > buf_len) + { + /* won't fit; free everything */ + GKI_freebuf(p_ccb->p_rx_msg); + p_ccb->p_rx_msg = NULL; + GKI_freebuf(p_buf); + p_ret = NULL; + } + else + { + /* copy contents of p_buf to p_rx_msg */ + memcpy((UINT8 *)(p_ccb->p_rx_msg + 1) + p_ccb->p_rx_msg->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + if (pkt_type == AVDT_PKT_TYPE_END) + { + p_ccb->p_rx_msg->offset -= p_ccb->p_rx_msg->len; + p_ccb->p_rx_msg->len += p_buf->len; + p_ret = p_ccb->p_rx_msg; + p_ccb->p_rx_msg = NULL; + } + else + { + p_ccb->p_rx_msg->offset += p_buf->len; + p_ccb->p_rx_msg->len += p_buf->len; + p_ret = NULL; + } + GKI_freebuf(p_buf); + } + } + } + return p_ret; +} + +/******************************************************************************* +** +** Function avdt_msg_send_cmd +** +** Description This function is called to send a command message. The +** sig_id parameter indicates the message type, p_params +** points to the message parameters, if any. It gets a buffer +** from the AVDTP command pool, executes the message building +** function for this message type. It then queues the message +** in the command queue for this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_cmd(tAVDT_CCB *p_ccb, void *p_scb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID); + if (p_buf == NULL) + { + AVDT_TRACE_ERROR0("avdt_msg_send_cmd out of buffer!!"); + return; + } + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* execute parameter building function to build message */ + (*avdt_msg_bld_cmd[sig_id - 1])(&p, p_params); + + /* set len */ + p_buf->len = (UINT16) (p - p_start); + + /* now store scb hdls, if any, in buf */ + if (p_scb != NULL) + { + p = (UINT8 *)(p_buf + 1); + + /* for start and suspend, p_scb points to array of handles */ + if ((sig_id == AVDT_SIG_START) || (sig_id == AVDT_SIG_SUSPEND)) + { + memcpy(p, (UINT8 *) p_scb, p_buf->len); + } + /* for all others, p_scb points to scb as usual */ + else + { + *p = avdt_scb_to_hdl((tAVDT_SCB *) p_scb); + } + } + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_CMD, p_ccb->label); + + /* increment label */ + p_ccb->label = (p_ccb->label + 1) % 16; + + /* queue message and trigger ccb to send it */ + GKI_enqueue(&p_ccb->cmd_q, p_buf); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + + +/******************************************************************************* +** +** Function avdt_msg_send_rsp +** +** Description This function is called to send a response message. The +** sig_id parameter indicates the message type, p_params +** points to the message parameters, if any. It gets a buffer +** from the AVDTP command pool, executes the message building +** function for this message type. It then queues the message +** in the response queue for this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_rsp(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID); + if (p_buf == NULL) return; + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* execute parameter building function to build message */ + (*avdt_msg_bld_rsp[sig_id - 1])(&p, p_params); + + /* set length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_RSP, p_params->hdr.label); + + /* queue message and trigger ccb to send it */ + GKI_enqueue(&p_ccb->rsp_q, p_buf); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + + +/******************************************************************************* +** +** Function avdt_msg_send_rej +** +** Description This function is called to send a reject message. The +** sig_id parameter indicates the message type. It gets +** a buffer from the AVDTP command pool and builds the +** message based on the message type and the error code. +** It then queues the message in the response queue for +** this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_rej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID); + if (p_buf == NULL) return; + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* if sig id included, build into message */ + if (sig_id != AVDT_SIG_NONE) + { + /* if this sig has a parameter, add the parameter */ + if ((sig_id == AVDT_SIG_SETCONFIG) || + (sig_id == AVDT_SIG_RECONFIG)) + { + AVDT_MSG_BLD_PARAM(p, p_params->hdr.err_param); + } + else if ((sig_id == AVDT_SIG_START) || + (sig_id == AVDT_SIG_SUSPEND)) + { + AVDT_MSG_BLD_SEID(p, p_params->hdr.err_param); + } + + /* add the error code */ + AVDT_MSG_BLD_ERR(p, p_params->hdr.err_code); + } + AVDT_TRACE_DEBUG0("avdt_msg_send_rej"); + + /* calculate length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_REJ, p_params->hdr.label); + + /* queue message and trigger ccb to send it */ + GKI_enqueue(&p_ccb->rsp_q, p_buf); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_msg_send_grej +** +** Description This function is called to send a general reject message. The +** sig_id parameter indicates the message type. It gets +** a buffer from the AVDTP command pool and builds the +** message based on the message type and the error code. +** It then queues the message in the response queue for +** this CCB. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_send_grej(tAVDT_CCB *p_ccb, UINT8 sig_id, tAVDT_MSG *p_params) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT8 *p_start; + + /* get a buffer */ + p_buf = (BT_HDR *) GKI_getpoolbuf(AVDT_CMD_POOL_ID); + if (p_buf == NULL) return; + + /* set up gki buf pointer and offset */ + p_buf->offset = AVDT_MSG_OFFSET; + p_start = p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* calculate length */ + p_buf->len = (UINT16) (p - p_start); + + /* stash sig, label, and message type in buf */ + p_buf->event = sig_id; + AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_GRJ, p_params->hdr.label); + //p_buf->event = 0; + //AVDT_BLD_LAYERSPEC(p_buf->layer_specific, 0, p_params->hdr.label); + AVDT_TRACE_DEBUG0("avdt_msg_send_grej"); + + /* queue message and trigger ccb to send it */ + GKI_enqueue(&p_ccb->rsp_q, p_buf); + avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_msg_ind +** +** Description This function is called by the adaption layer when an +** incoming message is received on the signaling channel. +** It parses the message and sends an event to the appropriate +** SCB or CCB for the message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_msg_ind(tAVDT_CCB *p_ccb, BT_HDR *p_buf) +{ + tAVDT_SCB *p_scb; + UINT8 *p; + BOOLEAN ok = TRUE; + BOOLEAN handle_rsp = FALSE; + BOOLEAN gen_rej = FALSE; + UINT8 label; + UINT8 pkt_type; + UINT8 msg_type; + UINT8 sig = 0; + tAVDT_MSG msg; + tAVDT_CFG cfg; + UINT8 err; + UINT8 evt = 0; + UINT8 scb_hdl; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_buf = avdt_msg_asmbl(p_ccb, p_buf)) == NULL) + { + return; + } + + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* parse the message header */ + AVDT_MSG_PRS_HDR(p, label, pkt_type, msg_type); + + /* AVDT_TRACE_DEBUG1("msg_type=%d", msg_type); */ + /* set up label and ccb_idx in message hdr */ + msg.hdr.label = label; + msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); + + /* verify msg type */ + if (msg_type == AVDT_MSG_TYPE_GRJ) + { + AVDT_TRACE_WARNING1("Dropping msg msg_type=%d", msg_type); + ok = FALSE; + } + /* check for general reject */ + else if ((msg_type == AVDT_MSG_TYPE_REJ) && (p_buf->len == AVDT_LEN_GEN_REJ)) + { + gen_rej = TRUE; + if (p_ccb->p_curr_cmd != NULL) + { + msg.hdr.sig_id = sig = (UINT8) p_ccb->p_curr_cmd->event; + evt = avdt_msg_rej_2_evt[sig - 1]; + msg.hdr.err_code = AVDT_ERR_NSC; + msg.hdr.err_param = 0; + } + } + else /* not a general reject */ + { + /* get and verify signal */ + AVDT_MSG_PRS_SIG(p, sig); + msg.hdr.sig_id = sig; + if ((sig == 0) || (sig > AVDT_SIG_MAX)) + { + AVDT_TRACE_WARNING2("Dropping msg sig=%d msg_type:%d", sig, msg_type); + ok = FALSE; + + /* send a general reject */ + if (msg_type == AVDT_MSG_TYPE_CMD) + { + avdt_msg_send_grej(p_ccb, sig, &msg); + } + } + } + + if (ok && !gen_rej) + { + /* skip over header (msg length already verified during reassembly) */ + p_buf->len -= AVDT_LEN_TYPE_SINGLE; + + /* set up to parse message */ + if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_DISCOVER)) + { + /* parse discover rsp message to struct supplied by app */ + msg.discover_rsp.p_sep_info = (tAVDT_SEP_INFO *) p_ccb->p_proc_data; + msg.discover_rsp.num_seps = p_ccb->proc_param; + } + else if ((msg_type == AVDT_MSG_TYPE_RSP) && + ((sig == AVDT_SIG_GETCAP) || (sig == AVDT_SIG_GET_ALLCAP))) + { + /* parse discover rsp message to struct supplied by app */ + msg.svccap.p_cfg = (tAVDT_CFG *) p_ccb->p_proc_data; + } + else if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_GETCONFIG)) + { + /* parse get config rsp message to struct allocated locally */ + msg.svccap.p_cfg = &cfg; + } + else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_SETCONFIG)) + { + /* parse config cmd message to struct allocated locally */ + msg.config_cmd.p_cfg = &cfg; + } + else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_RECONFIG)) + { + /* parse reconfig cmd message to struct allocated locally */ + msg.reconfig_cmd.p_cfg = &cfg; + } + + /* parse message; while we're at it map message sig to event */ + if (msg_type == AVDT_MSG_TYPE_CMD) + { + msg.hdr.err_code = err = (*avdt_msg_prs_cmd[sig - 1])(&msg, p, p_buf->len); + evt = avdt_msg_cmd_2_evt[sig - 1]; + } + else if (msg_type == AVDT_MSG_TYPE_RSP) + { + msg.hdr.err_code = err = (*avdt_msg_prs_rsp[sig - 1])(&msg, p, p_buf->len); + evt = avdt_msg_rsp_2_evt[sig - 1]; + } + else /* msg_type == AVDT_MSG_TYPE_REJ */ + { + err = avdt_msg_prs_rej(&msg, p, sig); + evt = avdt_msg_rej_2_evt[sig - 1]; + } + + /* if parsing failed */ + if (err != 0) + { + AVDT_TRACE_WARNING2("Parsing failed sig=%d err=0x%x", sig, err); + + /* if its a rsp or rej, drop it; if its a cmd, send a rej; + ** note special case for abort; never send abort reject + */ + ok = FALSE; + if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig != AVDT_SIG_ABORT)) + { + avdt_msg_send_rej(p_ccb, sig, &msg); + } + } + } + + /* if its a rsp or rej, check sent cmd to see if we're waiting for + ** the rsp or rej. If we didn't send a cmd for it, drop it. If + ** it does match a cmd, stop timer for the cmd. + */ + if (ok) + { + if ((msg_type == AVDT_MSG_TYPE_RSP) || (msg_type == AVDT_MSG_TYPE_REJ)) + { + if ((p_ccb->p_curr_cmd != NULL) && + (p_ccb->p_curr_cmd->event == sig) && + (AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_cmd->layer_specific) == label)) + { + /* stop timer */ + btu_stop_timer(&p_ccb->timer_entry); + + /* clear retransmission count */ + p_ccb->ret_count = 0; + + /* later in this function handle ccb event */ + handle_rsp = TRUE; + } + else + { + ok = FALSE; + AVDT_TRACE_WARNING2("Cmd not found for rsp sig=%d label=%d", sig, label); + } + } + } + + if (ok) + { + /* if it's a ccb event send to ccb */ + if (evt & AVDT_CCB_MKR) + { + avdt_ccb_event(p_ccb, (UINT8)(evt & ~AVDT_CCB_MKR), (tAVDT_CCB_EVT *) &msg); + } + /* if it's a scb event */ + else + { + /* Scb events always have a single seid. For cmd, get seid from + ** message. For rej and rsp, get seid from p_curr_cmd. + */ + if (msg_type == AVDT_MSG_TYPE_CMD) + { + scb_hdl = msg.single.seid; + } + else + { + scb_hdl = *((UINT8 *)(p_ccb->p_curr_cmd + 1)); + } + + /* Map seid to the scb and send it the event. For cmd, seid has + ** already been verified by parsing function. + */ + if (evt && (p_scb = avdt_scb_by_hdl(scb_hdl)) != NULL) + { + avdt_scb_event(p_scb, evt, (tAVDT_SCB_EVT *) &msg); + } + } + } + + /* free message buffer */ + GKI_freebuf(p_buf); + + /* if its a rsp or rej, send event to ccb to free associated + ** cmd msg buffer and handle cmd queue + */ + if (handle_rsp) + { + avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL); + } +} diff --git a/stack/avdt/avdt_scb.c b/stack/avdt/avdt_scb.c new file mode 100644 index 0000000..b44af06 --- /dev/null +++ b/stack/avdt/avdt_scb.c @@ -0,0 +1,800 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains the stream control block and functions which + * operate on the stream control block. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "gki.h" +#include "btu.h" + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ +#if AVDT_DEBUG == TRUE + +/* verbose state strings for trace */ +const char * const avdt_scb_st_str[] = { + "SCB_IDLE_ST", + "SCB_CONF_ST", + "SCB_OPENING_ST", + "SCB_OPEN_ST", + "SCB_STREAM_ST", + "SCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char * const avdt_scb_evt_str[] = { + "API_REMOVE_EVT", + "API_WRITE_REQ_EVT", + "API_GETCONFIG_REQ_EVT", + "API_DELAY_RPT_REQ", + "API_SETCONFIG_REQ_EVT", + "API_OPEN_REQ_EVT", + "API_CLOSE_REQ_EVT", + "API_RECONFIG_REQ_EVT", + "API_SECURITY_REQ_EVT", + "API_ABORT_REQ_EVT", + "API_GETCONFIG_RSP_EVT", + "API_SETCONFIG_RSP_EVT", + "API_SETCONFIG_REJ_EVT", + "API_OPEN_RSP_EVT", + "API_CLOSE_RSP_EVT", + "API_RECONFIG_RSP_EVT", + "API_SECURITY_RSP_EVT", + "API_ABORT_RSP_EVT", + "MSG_SETCONFIG_CMD_EVT", + "MSG_GETCONFIG_CMD_EVT", + "MSG_OPEN_CMD_EVT", + "MSG_START_CMD_EVT", + "MSG_SUSPEND_CMD_EVT", + "MSG_CLOSE_CMD_EVT", + "MSG_ABORT_CMD_EVT", + "MSG_RECONFIG_CMD_EVT", + "MSG_SECURITY_CMD_EVT", + "MSG_DELAY_RPT_CMD_EVT", + "MSG_DELAY_RPT_RSP_EVT", + "MSG_SETCONFIG_RSP_EVT", + "MSG_GETCONFIG_RSP_EVT", + "MSG_OPEN_RSP_EVT", + "MSG_START_RSP_EVT", + "MSG_SUSPEND_RSP_EVT", + "MSG_CLOSE_RSP_EVT", + "MSG_ABORT_RSP_EVT", + "MSG_RECONFIG_RSP_EVT", + "MSG_SECURITY_RSP_EVT", + "MSG_SETCONFIG_REJ_EVT", + "MSG_OPEN_REJ_EVT", + "MSG_START_REJ_EVT", + "MSG_SUSPEND_REJ_EVT", + "TC_TOUT_EVT", + "TC_OPEN_EVT", + "TC_CLOSE_EVT", + "TC_CONG_EVT", + "TC_DATA_EVT", + "CC_CLOSE_EVT" +}; + +#endif + + +/* action function list */ +const tAVDT_SCB_ACTION avdt_scb_action[] = { + avdt_scb_hdl_abort_cmd, + avdt_scb_hdl_abort_rsp, + avdt_scb_hdl_close_cmd, + avdt_scb_hdl_close_rsp, + avdt_scb_hdl_getconfig_cmd, + avdt_scb_hdl_getconfig_rsp, + avdt_scb_hdl_open_cmd, + avdt_scb_hdl_open_rej, + avdt_scb_hdl_open_rsp, + avdt_scb_hdl_pkt, + avdt_scb_drop_pkt, + avdt_scb_hdl_reconfig_cmd, + avdt_scb_hdl_reconfig_rsp, + avdt_scb_hdl_security_cmd, + avdt_scb_hdl_security_rsp, + avdt_scb_hdl_setconfig_cmd, + avdt_scb_hdl_setconfig_rej, + avdt_scb_hdl_setconfig_rsp, + avdt_scb_hdl_start_cmd, + avdt_scb_hdl_start_rsp, + avdt_scb_hdl_suspend_cmd, + avdt_scb_hdl_suspend_rsp, + avdt_scb_hdl_tc_close, +#if AVDT_REPORTING == TRUE + avdt_scb_hdl_tc_close_sto, +#endif + avdt_scb_hdl_tc_open, +#if AVDT_REPORTING == TRUE + avdt_scb_hdl_tc_open_sto, +#endif + avdt_scb_snd_delay_rpt_req, + avdt_scb_hdl_delay_rpt_cmd, + avdt_scb_hdl_delay_rpt_rsp, + avdt_scb_hdl_write_req, + avdt_scb_snd_abort_req, + avdt_scb_snd_abort_rsp, + avdt_scb_snd_close_req, + avdt_scb_snd_stream_close, + avdt_scb_snd_close_rsp, + avdt_scb_snd_getconfig_req, + avdt_scb_snd_getconfig_rsp, + avdt_scb_snd_open_req, + avdt_scb_snd_open_rsp, + avdt_scb_snd_reconfig_req, + avdt_scb_snd_reconfig_rsp, + avdt_scb_snd_security_req, + avdt_scb_snd_security_rsp, + avdt_scb_snd_setconfig_req, + avdt_scb_snd_setconfig_rej, + avdt_scb_snd_setconfig_rsp, + avdt_scb_snd_tc_close, + avdt_scb_cb_err, + avdt_scb_cong_state, + avdt_scb_rej_state, + avdt_scb_rej_in_use, + avdt_scb_rej_not_in_use, + avdt_scb_set_remove, + avdt_scb_free_pkt, + avdt_scb_clr_pkt, + avdt_scb_chk_snd_pkt, + avdt_scb_tc_timer, + avdt_scb_clr_vars, + avdt_scb_dealloc +}; + +/* state table information */ +#define AVDT_SCB_ACTIONS 2 /* number of actions */ +#define AVDT_SCB_NEXT_STATE 2 /* position of next state */ +#define AVDT_SCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avdt_scb_st_idle[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_DEALLOC, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_SND_SETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_SND_SETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_SND_SETCONFIG_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_SETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_NOT_IN_USE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_SETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_HDL_SETCONFIG_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_CLR_VARS, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST} +}; + +/* state table for configured state */ +const UINT8 avdt_scb_st_conf[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CONF_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_SND_OPEN_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_SND_OPEN_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IDLE_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_IN_USE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_HDL_OPEN_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_HDL_OPEN_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IDLE_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_HDL_OPEN_REJ, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CONF_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avdt_scb_st_opening[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_CLOSING_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* TC_OPEN_EVT */ {AVDT_SCB_HDL_TC_OPEN, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPENING_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST} +}; + +/* state table for open state */ +const UINT8 avdt_scb_st_open[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_CLOSE_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_SND_RECONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_SND_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_HDL_START_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_HDL_RECONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_HDL_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#if AVDT_REPORTING == TRUE +/* TC_OPEN_EVT */ {AVDT_SCB_HDL_TC_OPEN_STO, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE_STO, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#else +/* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +#endif +/* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_OPEN_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST} +}; + +/* state table for streaming state */ +const UINT8 avdt_scb_st_stream[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_SND_STREAM_CLOSE, AVDT_SCB_SET_REMOVE, AVDT_SCB_CLOSING_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_HDL_WRITE_REQ, AVDT_SCB_CHK_SND_PKT, AVDT_SCB_STREAM_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_SND_GETCONFIG_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_SND_DELAY_RPT_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_SND_STREAM_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_SND_SECURITY_REQ, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_SND_ABORT_REQ, AVDT_SCB_CLR_PKT, AVDT_SCB_CLOSING_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_SND_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_SND_CLOSE_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_SND_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_TC_TIMER, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_HDL_GETCONFIG_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_HDL_SUSPEND_CMD, AVDT_SCB_CLR_PKT, AVDT_SCB_OPEN_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_HDL_CLOSE_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_CLR_PKT, AVDT_SCB_STREAM_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_HDL_SECURITY_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_HDL_DELAY_RPT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_HDL_GETCONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_CLR_PKT, AVDT_SCB_OPEN_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_HDL_RECONFIG_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_HDL_SECURITY_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* TC_OPEN_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_CONG_EVT */ {AVDT_SCB_CONG_STATE, AVDT_SCB_CHK_SND_PKT, AVDT_SCB_STREAM_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_HDL_PKT, AVDT_SCB_IGNORE, AVDT_SCB_STREAM_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST} +}; + +/* state table for closing state */ +const UINT8 avdt_scb_st_closing[][AVDT_SCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* API_REMOVE_EVT */ {AVDT_SCB_SET_REMOVE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_WRITE_REQ_EVT */ {AVDT_SCB_FREE_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_GETCONFIG_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_DELAY_RPT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_SETCONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_OPEN_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_CLOSE_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_SECURITY_REQ_EVT */ {AVDT_SCB_CB_ERR, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_ABORT_REQ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_CLOSE_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* API_ABORT_RSP_EVT */ {AVDT_SCB_SND_ABORT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_GETCONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_OPEN_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_START_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SUSPEND_CMD_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_CLOSE_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_ABORT_CMD_EVT */ {AVDT_SCB_HDL_ABORT_CMD, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_RECONFIG_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SECURITY_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_DELAY_RPT_CMD_EVT */ {AVDT_SCB_REJ_STATE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_DELAY_RPT_RSP_EVT */ {AVDT_SCB_HDL_DELAY_RPT_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_GETCONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_OPEN_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_START_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SUSPEND_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_CLOSE_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_CLOSE_RSP, AVDT_SCB_CLOSING_ST}, +/* MSG_ABORT_RSP_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_HDL_ABORT_RSP, AVDT_SCB_CLOSING_ST}, +/* MSG_RECONFIG_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SECURITY_RSP_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SETCONFIG_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_OPEN_REJ_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_START_REJ_EVT */ {AVDT_SCB_HDL_START_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* MSG_SUSPEND_REJ_EVT */ {AVDT_SCB_HDL_SUSPEND_RSP, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* TC_TOUT_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* TC_OPEN_EVT */ {AVDT_SCB_SND_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* TC_CLOSE_EVT */ {AVDT_SCB_HDL_TC_CLOSE, AVDT_SCB_IGNORE, AVDT_SCB_IDLE_ST}, +/* TC_CONG_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* TC_DATA_EVT */ {AVDT_SCB_DROP_PKT, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST}, +/* CC_CLOSE_EVT */ {AVDT_SCB_IGNORE, AVDT_SCB_IGNORE, AVDT_SCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVDT_SCB_ST_TBL)[AVDT_SCB_NUM_COLS]; + +/* state table */ +const tAVDT_SCB_ST_TBL avdt_scb_st_tbl[] = { + avdt_scb_st_idle, + avdt_scb_st_conf, + avdt_scb_st_opening, + avdt_scb_st_open, + avdt_scb_st_stream, + avdt_scb_st_closing +}; + + +/******************************************************************************* +** +** Function avdt_scb_event +** +** Description State machine event handling function for scb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_event(tAVDT_SCB *p_scb, UINT8 event, tAVDT_SCB_EVT *p_data) +{ + tAVDT_SCB_ST_TBL state_table; + UINT8 action; + int i; + +#if AVDT_DEBUG == TRUE + AVDT_TRACE_EVENT4("SCB hdl=%d event=%d/%s state=%s", avdt_scb_to_hdl(p_scb), event, avdt_scb_evt_str[event], avdt_scb_st_str[p_scb->state]); +#endif + BTTRC_AVDT_SCB_EVENT(event, p_scb->state); + + /* set current event */ + p_scb->curr_evt = event; + + /* look up the state table for the current state */ + state_table = avdt_scb_st_tbl[p_scb->state]; + + /* set next state */ + if (p_scb->state != state_table[event][AVDT_SCB_NEXT_STATE]) + BTTRC_AVDT_SCB_STATE(state_table[event][AVDT_SCB_NEXT_STATE]); + p_scb->state = state_table[event][AVDT_SCB_NEXT_STATE]; + + + /* execute action functions */ + for (i = 0; i < AVDT_SCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVDT_SCB_IGNORE) + { + BTTRC_AVDT_SCB_ACTION(action); + (*avdt_cb.p_scb_act[action])(p_scb, p_data); + } + else + { + break; + } + } +} + + +/******************************************************************************* +** +** Function avdt_scb_init +** +** Description Initialize stream control block module. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_init(void) +{ + memset(&avdt_cb.scb[0], 0, sizeof(tAVDT_SCB) * AVDT_NUM_SEPS); + avdt_cb.p_scb_act = (tAVDT_SCB_ACTION *) avdt_scb_action; +} + + +/******************************************************************************* +** +** Function avdt_scb_alloc +** +** Description Allocate a stream control block. +** +** +** Returns pointer to the scb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVDT_SCB *avdt_scb_alloc(tAVDT_CS *p_cs) +{ + tAVDT_SCB *p_scb = &avdt_cb.scb[0]; + int i; + + /* find available scb */ + for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) + { + if (!p_scb->allocated) + { + memset(p_scb,0,sizeof(tAVDT_SCB)); + p_scb->allocated = TRUE; + p_scb->p_ccb = NULL; + memcpy(&p_scb->cs, p_cs, sizeof(tAVDT_CS)); +#if AVDT_MULTIPLEXING == TRUE + /* initialize fragments gueue */ + GKI_init_q(&p_scb->frag_q); + + if(p_cs->cfg.psc_mask & AVDT_PSC_MUX) + { + p_scb->cs.cfg.mux_tcid_media = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); +#if AVDT_REPORTING == TRUE + if(p_cs->cfg.psc_mask & AVDT_PSC_REPORT) + { + p_scb->cs.cfg.mux_tcid_report = avdt_ad_type_to_tcid(AVDT_CHAN_REPORT, p_scb); + } +#endif + } +#endif + p_scb->timer_entry.param = (UINT32) p_scb; + AVDT_TRACE_DEBUG2("avdt_scb_alloc hdl=%d, psc_mask:0x%x", i+1, p_cs->cfg.psc_mask); + break; + } + } + + if (i == AVDT_NUM_SEPS) + { + /* out of ccbs */ + p_scb = NULL; + AVDT_TRACE_WARNING0("Out of scbs"); + } + + return p_scb; +} + +/******************************************************************************* +** +** Function avdt_scb_dealloc +** +** Description Deallocate a stream control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_MULTIPLEXING == TRUE + void *p_buf; +#endif + + AVDT_TRACE_DEBUG1("avdt_scb_dealloc hdl=%d", avdt_scb_to_hdl(p_scb)); + btu_stop_timer(&p_scb->timer_entry); + +#if AVDT_MULTIPLEXING == TRUE + /* free fragments we're holding, if any; it shouldn't happen */ + while ((p_buf = GKI_dequeue (&p_scb->frag_q)) != NULL) + GKI_freebuf(p_buf); +#endif + + memset(p_scb, 0, sizeof(tAVDT_SCB)); +} + +/******************************************************************************* +** +** Function avdt_scb_to_hdl +** +** Description Given a pointer to an scb, return its handle (or seid). +** +** +** Returns Index of scb. +** +*******************************************************************************/ +UINT8 avdt_scb_to_hdl(tAVDT_SCB *p_scb) +{ + return (UINT8) (p_scb - avdt_cb.scb + 1); +} + +/******************************************************************************* +** +** Function avdt_scb_by_hdl +** +** Description Given an scb handle (or seid), return a pointer to the scb. +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +tAVDT_SCB *avdt_scb_by_hdl(UINT8 hdl) +{ + tAVDT_SCB *p_scb; + + /* verify index */ + if ((hdl > 0) && (hdl <= AVDT_NUM_SEPS)) + { + p_scb = &avdt_cb.scb[hdl - 1]; + + /* verify scb is allocated */ + if (!p_scb->allocated) + { + p_scb = NULL; + AVDT_TRACE_WARNING1("scb hdl %d not allocated", hdl); + } + } + else + { + p_scb = NULL; + AVDT_TRACE_WARNING1("scb hdl %d out of range", hdl); + } + return p_scb; +} + +/******************************************************************************* +** +** Function avdt_scb_verify +** +** Description Verify the condition of a list of scbs. +** +** +** Returns SEID that failed, or 0 if success. +** +*******************************************************************************/ +UINT8 avdt_scb_verify(tAVDT_CCB *p_ccb, UINT8 state, UINT8 *p_seid, UINT16 num_seid, UINT8 *p_err_code) +{ + int i; + tAVDT_SCB *p_scb; + UINT8 nsc_mask; + UINT8 chk_state; + UINT8 ret = 0; + + AVDT_TRACE_DEBUG1("avdt_scb_verify state %d", state); + /* set nonsupported command mask */ + /* translate public state into private state */ + nsc_mask = 0; + chk_state = AVDT_SCB_STREAM_ST; + switch(state) + { + case AVDT_VERIFY_SUSPEND: + nsc_mask = AVDT_NSC_SUSPEND; + break; + case AVDT_VERIFY_OPEN: + case AVDT_VERIFY_START: + chk_state = AVDT_SCB_OPEN_ST; + break; + } + + /* verify every scb */ + for (i = 0; i < num_seid; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_seid[i])) == NULL) + { + *p_err_code = AVDT_ERR_BAD_STATE; + break; + } + else if ((p_scb->state != chk_state) || (p_scb->p_ccb != p_ccb)) + { + *p_err_code = AVDT_ERR_BAD_STATE; + break; + } + else if (p_scb->cs.nsc_mask & nsc_mask) + { + *p_err_code = AVDT_ERR_NSC; + break; + } + } + + if (i != num_seid) + { + ret = p_seid[i]; + } + AVDT_TRACE_DEBUG3("avdt_scb_verify state %d, nsc_mask0x%x, ret: %d", + chk_state, nsc_mask, ret); + return ret; +} + +/******************************************************************************* +** +** Function avdt_scb_peer_seid_list +** +** Description Given a list of SCB handles, return a list of peer SEIDs +** for the handles, copied in place into the struct passed in. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_peer_seid_list(tAVDT_MULTI *p_multi) +{ + int i; + tAVDT_SCB *p_scb; + + for (i = 0; i < p_multi->num_seps; i++) + { + if ((p_scb = avdt_scb_by_hdl(p_multi->seid_list[i])) != NULL) + { + p_multi->seid_list[i] = p_scb->peer_seid; + } + } +} + diff --git a/stack/avdt/avdt_scb_act.c b/stack/avdt/avdt_scb_act.c new file mode 100644 index 0000000..c2ced74 --- /dev/null +++ b/stack/avdt/avdt_scb_act.c @@ -0,0 +1,2124 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 module contains the action functions associated with the stream + * control block state machine. + * + ******************************************************************************/ + +#include +#include "data_types.h" +#include "bt_target.h" +#include "avdt_api.h" +#include "avdtc_api.h" +#include "avdt_int.h" +#include "gki.h" +#include "btu.h" + +/* This table is used to lookup the callback event that matches a particular +** state machine API request event. Note that state machine API request +** events are at the beginning of the event list starting at zero, thus +** allowing for this table. +*/ +const UINT8 avdt_scb_cback_evt[] = { + 0, /* API_REMOVE_EVT (no event) */ + AVDT_WRITE_CFM_EVT, /* API_WRITE_REQ_EVT */ + 0, /* API_GETCONFIG_REQ_EVT (no event) */ + 0, /* API_DELAY_RPT_REQ_EVT (no event) */ + AVDT_OPEN_CFM_EVT, /* API_SETCONFIG_REQ_EVT */ + AVDT_OPEN_CFM_EVT, /* API_OPEN_REQ_EVT */ + AVDT_CLOSE_CFM_EVT, /* API_CLOSE_REQ_EVT */ + AVDT_RECONFIG_CFM_EVT, /* API_RECONFIG_REQ_EVT */ + AVDT_SECURITY_CFM_EVT, /* API_SECURITY_REQ_EVT */ + 0 /* API_ABORT_REQ_EVT (no event) */ +}; + +/* This table is used to look up the callback event based on the signaling +** role when the stream is closed. +*/ +const UINT8 avdt_scb_role_evt[] = { + AVDT_CLOSE_IND_EVT, /* AVDT_CLOSE_ACP */ + AVDT_CLOSE_CFM_EVT, /* AVDT_CLOSE_INT */ + AVDT_CLOSE_IND_EVT, /* AVDT_OPEN_ACP */ + AVDT_OPEN_CFM_EVT /* AVDT_OPEN_INT */ +}; + +/******************************************************************************* +** +** Function avdt_scb_gen_ssrc +** +** Description This function generates a SSRC number unique to the stream. +** +** Returns SSRC value. +** +*******************************************************************************/ +UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb) +{ + /* combine the value of the media type and codec type of the SCB */ + return ((UINT32)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.cfg.codec_info[2])); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_abort_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_ABORT_RSP_EVT +** to initiate sending of an abort response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->role = AVDT_CLOSE_ACP; + avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_abort_rsp +** +** Description This function is an empty function; it serves as a +** placeholder for a conformance API action function. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + return; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_close_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_CLOSE_RSP_EVT +** to initiate sending of a close response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->role = AVDT_CLOSE_ACP; + avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_close_rsp +** +** Description This function sets the close_code variable to the error +** code returned in the close response. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->close_code = p_data->msg.hdr.err_code; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_getconfig_cmd +** +** Description This function retrieves the configuration parameters of +** the SCB and sends the SCB an AVDT_SCB_API_GETCONFIG_RSP_EVT +** to initiate sending of a get configuration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb,tAVDT_SCB_EVT *p_data) +{ + p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; + + avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_getconfig_rsp +** +** Description This function is an empty function; it serves as a +** placeholder for a conformance API action function. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + return; +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_cmd +** +** Description This function sends the SCB an AVDT_SCB_API_OPEN_RSP_EVT +** to initiate sending of an open response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_rej +** +** Description This function calls the application callback function +** indicating the open request has failed. It initializes +** certain SCB variables and sends a AVDT_CCB_UL_CLOSE_EVT +** to the CCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* do exactly same as setconfig reject */ + avdt_scb_hdl_setconfig_rej(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_open_rsp +** +** Description This function calls avdt_ad_open_req() to initiate +** connection of the transport channel for this stream. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* initiate opening of trans channels for this SEID */ + p_scb->role = AVDT_OPEN_INT; + avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT); + + /* start tc connect timer */ + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt_no_frag +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p, *p_start; + UINT8 o_v, o_p, o_x, o_cc; + UINT8 m_pt; + UINT8 marker; + UINT16 seq; + UINT32 time_stamp; + UINT32 ssrc; + UINT16 offset; + UINT16 ex_len; + UINT8 pad_len = 0; + + p = p_start = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + + /* parse media packet header */ + AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc); + AVDT_MSG_PRS_M_PT(p, m_pt, marker); + BE_STREAM_TO_UINT16(seq, p); + BE_STREAM_TO_UINT32(time_stamp, p); + BE_STREAM_TO_UINT32(ssrc, p); + + /* skip over any csrc's in packet */ + p += o_cc * 4; + + /* check for and skip over extension header */ + if (o_x) + { + p += 2; + BE_STREAM_TO_UINT16(ex_len, p); + p += ex_len * 4; + } + + /* save our new offset */ + offset = (UINT16) (p - p_start); + + /* adjust length for any padding at end of packet */ + if (o_p) + { + /* padding length in last byte of packet */ + pad_len = *(p_start + p_data->p_pkt->len); + } + + /* do sanity check */ + if ((offset > p_data->p_pkt->len) || ((pad_len + offset) > p_data->p_pkt->len)) + { + AVDT_TRACE_WARNING0("Got bad media packet"); + GKI_freebuf(p_data->p_pkt); + } + /* adjust offset and length and send it up */ + else + { + p_data->p_pkt->len -= (offset + pad_len); + p_data->p_pkt->offset += offset; + + if (p_scb->cs.p_data_cback != NULL) + { + /* report sequence number */ + p_data->p_pkt->layer_specific = seq; + (*p_scb->cs.p_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt, + time_stamp, (UINT8)(m_pt | (marker<<7))); + } + else + { +#if AVDT_MULTIPLEXING == TRUE + if ((p_scb->cs.p_media_cback != NULL) + && (p_scb->p_media_buf != NULL) + && (p_scb->media_buf_len > p_data->p_pkt->len)) + { + /* media buffer enough length is assigned by application. Lets use it*/ + memcpy(p_scb->p_media_buf,(UINT8*)(p_data->p_pkt + 1) + p_data->p_pkt->offset, + p_data->p_pkt->len); + (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb),p_scb->p_media_buf, + p_scb->media_buf_len,time_stamp,seq,m_pt,marker); + } +#endif + GKI_freebuf(p_data->p_pkt); + } + } +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_report +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +UINT8 * avdt_scb_hdl_report(tAVDT_SCB *p_scb, UINT8 *p, UINT16 len) +{ + UINT16 result = AVDT_SUCCESS; + UINT8 *p_start = p; + UINT32 ssrc; + UINT8 o_v, o_p, o_cc; + UINT16 pkt_len; + AVDT_REPORT_TYPE pt; + tAVDT_REPORT_DATA report, *p_rpt; + + AVDT_TRACE_DEBUG0( "avdt_scb_hdl_report"); + if(p_scb->cs.p_report_cback) + { + p_rpt = &report; + /* parse report packet header */ + AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc); + pt = *p++; + BE_STREAM_TO_UINT16(pkt_len, p); + BE_STREAM_TO_UINT32(ssrc, p); + + switch(pt) + { + case AVDT_RTCP_PT_SR: /* the packet type - SR (Sender Report) */ + BE_STREAM_TO_UINT32(report.sr.ntp_sec, p); + BE_STREAM_TO_UINT32(report.sr.ntp_frac, p); + BE_STREAM_TO_UINT32(report.sr.rtp_time, p); + BE_STREAM_TO_UINT32(report.sr.pkt_count, p); + BE_STREAM_TO_UINT32(report.sr.octet_count, p); + break; + + case AVDT_RTCP_PT_RR: /* the packet type - RR (Receiver Report) */ + report.rr.frag_lost = *p; + BE_STREAM_TO_UINT32(report.rr.packet_lost, p); + report.rr.packet_lost &= 0xFFFFFF; + BE_STREAM_TO_UINT32(report.rr.seq_num_rcvd, p); + BE_STREAM_TO_UINT32(report.rr.jitter, p); + BE_STREAM_TO_UINT32(report.rr.lsr, p); + BE_STREAM_TO_UINT32(report.rr.dlsr, p); + break; + + case AVDT_RTCP_PT_SDES: /* the packet type - SDES (Source Description) */ + if(*p == AVDT_RTCP_SDES_CNAME) + { + p_rpt = (tAVDT_REPORT_DATA *)(p+2); + } + else + { + AVDT_TRACE_WARNING5( " - SDES SSRC=0x%08x sc=%d %d len=%d %s", + ssrc, o_cc, *p, *(p+1), p+2); + result = AVDT_BUSY; + } + break; + + default: + AVDT_TRACE_ERROR1( "Bad Report pkt - packet type: %d", pt); + result = AVDT_BAD_PARAMS; + } + + if(result == AVDT_SUCCESS) + (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, p_rpt); + + } + p_start += len; + return p_start; +} +#endif + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt_frag +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* Fields of Adaptation Layer Header */ + UINT8 al_tsid,al_frag,al_lcode; + UINT16 al_len; + /* media header fields */ + UINT8 o_v, o_p, o_x, o_cc; + UINT8 m_pt; + UINT8 marker; + UINT16 seq; + UINT32 time_stamp; + UINT32 ssrc; + UINT16 ex_len; + UINT8 pad_len; + /* other variables */ + UINT8 *p; /* current pointer */ + UINT8 *p_end; /* end of all packet */ + UINT8 *p_payload; /* pointer to media fragment payload in the buffer */ + UINT32 payload_len; /* payload length */ + UINT16 frag_len; /* fragment length */ + + p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + p_end = p + p_data->p_pkt->len; + /* parse all fragments */ + while(p < p_end) + { + if (p_end - p < 4) /* length check. maximum length of AL header = 4 */ + { + AVDT_TRACE_WARNING2("p_end: 0x%x - p:0x%x < 4", p_end, p); + break; + } + + /* parse first byte */ + al_tsid = (*p)>>3; + al_frag = ( (*p) >> 2 ) & 0x01; + al_lcode = (*p++) & AVDT_ALH_LCODE_MASK; + + /* in case of TSID=00000, a second AL header byte, before the length field, + ** is expected and contains the actual TSID, aligned with MSB */ + if(al_tsid == 0) + al_tsid = *p++; + + /* get remaining media length on base of lcode */ + switch(al_lcode) + { + case AVDT_ALH_LCODE_NONE: /* No length field present. Take length from l2cap */ + al_len = (UINT16)(p_end - p); + break; + case AVDT_ALH_LCODE_16BIT: /* 16 bit length field */ + BE_STREAM_TO_UINT16(al_len, p); + break; + case AVDT_ALH_LCODE_9BITM0: /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */ + al_len = *p++; + break; + default: /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */ + al_len =(UINT16)*p++ + 0x100; + } + + /* max fragment length */ + frag_len = (UINT16)(p_end - p); + /* if it isn't last fragment */ + if(frag_len >= al_len) + frag_len = al_len; + + /* check TSID corresponds to config */ + if (al_tsid != p_scb->curr_cfg.mux_tsid_media) + { +#if AVDT_REPORTING == TRUE + if((p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) && + (al_tsid == p_scb->curr_cfg.mux_tsid_report)) + { + /* parse reporting packet */ + p = avdt_scb_hdl_report(p_scb, p, frag_len); + continue; + } + else +#endif + { + AVDT_TRACE_WARNING2("bad tsid: %d, mux_tsid_media:%d", al_tsid, p_scb->curr_cfg.mux_tsid_media); + break; + } + } + /* check are buffer for assembling and related callback set */ + else if ((p_scb->p_media_buf == NULL) || (p_scb->cs.p_media_cback == NULL)) + { + AVDT_TRACE_WARNING0("NULL p_media_buf or p_media_cback"); + break; + } + + + /* it is media fragment beginning */ + if(!al_frag) /* is it first fragment of original media packet */ + { + AVDT_TRACE_DEBUG2("al:%d media:%d", + al_len, p_scb->media_buf_len); + + p_scb->frag_off = 0; + p_scb->frag_org_len = al_len; /* total length of original media packet */ + /* length check: minimum length of media header is 12 */ + if (p_scb->frag_org_len < 12) + { + AVDT_TRACE_WARNING1("bad al_len: %d(<12)", al_len); + break; + } + /* check that data fit into buffer */ + if (al_len > p_scb->media_buf_len) + { + AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len); + break; + } + /* make sure it is the last fragment in l2cap packet */ + if (p + al_len < p_end) + { + AVDT_TRACE_WARNING2("bad al_len: %d(>%d)", al_len, p_scb->media_buf_len); + break; + } + } + else + { + AVDT_TRACE_DEBUG4("al:%d media:%d frag_org_len:%d frag_off:%d", + al_len, p_scb->media_buf_len, p_scb->frag_org_len, p_scb->frag_off); + + /* check that remaining length from AL header equals to original len - length of already received fragments */ + if(al_len != p_scb->frag_org_len - p_scb->frag_off) + { + AVDT_TRACE_WARNING4("al_len:%d != (frag_org_len:%d - frag_off:%d) %d", + al_len, p_scb->frag_org_len, p_scb->frag_off, + (p_scb->frag_org_len- p_scb->frag_off)); + break; + } + + /* do sanity check */ + if (p_scb->frag_off == 0) + { + AVDT_TRACE_WARNING0("frag_off=0"); + break; + } + } + /* do common sanity check */ + if((p_scb->frag_org_len <= p_scb->frag_off) || (p_scb->frag_org_len >= p_scb->media_buf_len)) + { + AVDT_TRACE_WARNING3("common sanity frag_off:%d frag_org_len:%d media_buf_len:%d", + p_scb->frag_off, p_scb->frag_org_len, p_scb->media_buf_len); + break; + } + + AVDT_TRACE_DEBUG4("Received fragment org_len=%d off=%d al_len=%d frag_len=%d", + p_scb->frag_org_len, p_scb->frag_off, al_len, frag_len); + + /* copy fragment into buffer */ + memcpy(p_scb->p_media_buf + p_scb->frag_off, p, frag_len); + p_scb->frag_off += frag_len; + /* move to the next fragment */ + p += frag_len; + /* if it is last fragment in original media packet then process total media pocket */ + if(p_scb->frag_off == p_scb->frag_org_len) + { + p_payload = p_scb->p_media_buf; + + /* media header */ + AVDT_MSG_PRS_OCTET1(p_payload, o_v, o_p, o_x, o_cc); + AVDT_MSG_PRS_M_PT(p_payload, m_pt, marker); + BE_STREAM_TO_UINT16(seq, p_payload); + BE_STREAM_TO_UINT32(time_stamp, p_payload); + BE_STREAM_TO_UINT32(ssrc, p_payload); + + /* skip over any csrc's in packet */ + p_payload += o_cc * 4; + + /* check for and skip over extension header */ + if (o_x) + { + if(p_scb->p_media_buf + p_scb->frag_off - p_payload < 4) + { + AVDT_TRACE_WARNING3("length check frag_off:%d p_media_buf:%d p_payload:%d", + p_scb->frag_off, p_scb->p_media_buf, p_payload); + break;/* length check */ + } + p_payload += 2; + BE_STREAM_TO_UINT16(ex_len, p_payload); + p_payload += ex_len * 4; + } + + if(p_payload >= p_scb->p_media_buf + p_scb->frag_off) + { + AVDT_TRACE_WARNING3("length check2 frag_off:%d p_media_buf:%d p_payload:%d", + p_scb->frag_off, p_scb->p_media_buf, p_payload); + break;/* length check */ + } + + /* adjust length for any padding at end of packet */ + if (o_p) + { + /* padding length in last byte of packet */ + pad_len = *(p_scb->p_media_buf + p_scb->frag_off - 1); + } + else + pad_len = 0; + /* payload length */ + payload_len = (UINT32)(p_scb->p_media_buf + p_scb->frag_off - pad_len - p_payload); + + AVDT_TRACE_DEBUG2("Received last fragment header=%d len=%d", + p_payload - p_scb->p_media_buf,payload_len); + + /* send total media packet up */ + if (p_scb->cs.p_media_cback != NULL) + { + (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_payload, + payload_len, time_stamp, seq, m_pt, marker); + } + } + } /* while(p < p_end) */ + + if(p < p_end) + { + AVDT_TRACE_WARNING0("*** Got bad media packet"); + } + GKI_freebuf(p_data->p_pkt); +} +#endif + +/******************************************************************************* +** +** Function avdt_scb_hdl_pkt +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_REPORTING == TRUE + UINT8 *p; +#endif + +#if AVDT_MULTIPLEXING == TRUE + /* select right function in dependance of is fragmentation supported or not */ + if( 0 != (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX)) + { + avdt_scb_hdl_pkt_frag(p_scb, p_data); + } + else +#endif +#if AVDT_REPORTING == TRUE + if(p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT) + { + p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset; + avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len); + GKI_freebuf(p_data->p_pkt); + } + else +#endif + avdt_scb_hdl_pkt_no_frag(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_drop_pkt +** +** Description Drop an incoming media packet. This function is called if +** a media packet is received in any state besides streaming. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + GKI_freebuf(p_data->p_pkt); + AVDT_TRACE_WARNING0("Dropped incoming media packet"); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_reconfig_cmd +** +** Description This function calls the application callback function +** with a reconfiguration indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* if command not supported */ + if (p_scb->cs.nsc_mask & AVDT_NSC_RECONFIG) + { + /* send reject */ + p_data->msg.hdr.err_code = AVDT_ERR_NSC; + p_data->msg.hdr.err_param = 0; + avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data); + } + else + { + /* store requested configuration */ + memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG)); + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_RECONFIG_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.reconfig_cmd); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_reconfig_rsp +** +** Description This function calls the application callback function +** with a reconfiguration confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) + { + /* store new configuration */ + if (p_scb->req_cfg.num_codec > 0) + { + p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; + memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE); + } + if (p_scb->req_cfg.num_protect > 0) + { + p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; + memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE); + } + } + + p_data->msg.svccap.p_cfg = &p_scb->curr_cfg; + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_RECONFIG_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.svccap); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_security_cmd +** +** Description This function calls the application callback with a +** security indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* if command not supported */ + if (p_scb->cs.nsc_mask & AVDT_NSC_SECURITY) + { + /* send reject */ + p_data->msg.hdr.err_code = AVDT_ERR_NSC; + avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data); + } + else + { + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_SECURITY_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.security_cmd); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_security_rsp +** +** Description This function calls the application callback with a +** security confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_SECURITY_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.security_cmd); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_cmd +** +** Description This function marks the SCB as in use and copies the +** configuration and peer SEID to the SCB. It then calls +** the application callback with a configuration indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CFG *p_cfg; + + if (!p_scb->in_use) + { + p_cfg = p_data->msg.config_cmd.p_cfg; + if(p_scb->cs.cfg.codec_info[AVDT_CODEC_TYPE_INDEX] == p_cfg->codec_info[AVDT_CODEC_TYPE_INDEX]) + { + /* set sep as in use */ + p_scb->in_use = TRUE; + + /* copy info to scb */ + p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); + p_scb->peer_seid = p_data->msg.config_cmd.int_seid; + memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG)); + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_CONFIG_IND_EVT, + (tAVDT_CTRL *) &p_data->msg.config_cmd); + } + else + { + p_data->msg.hdr.err_code = AVDT_ERR_UNSUP_CFG; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); + } + } + else + { + avdt_scb_rej_in_use(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_rej +** +** Description This function marks the SCB as not in use and calls the +** application callback with an open confirm indicating failure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* clear scb variables */ + avdt_scb_clr_vars(p_scb, p_data); + + /* tell ccb we're done with signaling channel */ + avdt_ccb_event(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_CCB_UL_CLOSE_EVT, NULL); + + /* call application callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + AVDT_OPEN_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_setconfig_rsp +** +** Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT +** to initiate sending of an open command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR single; + + if (p_scb->p_ccb != NULL) + { + /* save configuration */ + memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); + + /* initiate open */ + single.seid = p_scb->peer_seid; + avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single); + } +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_start_cmd +** +** Description This function calls the application callback with a +** start indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_START_IND_EVT, + NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_start_rsp +** +** Description This function calls the application callback with a +** start confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_START_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_suspend_cmd +** +** Description This function calls the application callback with a suspend +** indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_SUSPEND_IND_EVT, + NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_suspend_rsp +** +** Description This function calls the application callback with a suspend +** confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_SUSPEND_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_close +** +** Description This function is called when the transport channel is +** closed. It marks the SCB as not in use and +** initializes certain SCB parameters. It then sends +** an AVDT_CCB_UL_CLOSE_EVT to the CCB if the SCB +** initiated the close. It then checks to see if the SCB +** is to be removed. If it is it deallocates the SCB. Finally, +** it calls the application callback with a close indication. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 hdl = avdt_scb_to_hdl(p_scb); + tAVDT_CTRL_CBACK *p_ctrl_cback = p_scb->cs.p_ctrl_cback; + tAVDT_CTRL avdt_ctrl; + UINT8 event; + tAVDT_CCB *p_ccb = p_scb->p_ccb; + BD_ADDR remote_addr; + + + memcpy (remote_addr, p_ccb->peer_addr, BD_ADDR_LEN); + + /* set up hdr */ + avdt_ctrl.hdr.err_code = p_scb->close_code; + + /* clear sep variables */ + avdt_scb_clr_vars(p_scb, p_data); + p_scb->media_seq = 0; + p_scb->cong = FALSE; + + /* free pkt we're holding, if any */ + if (p_scb->p_pkt != NULL) + { + GKI_freebuf(p_scb->p_pkt); + p_scb->p_pkt = NULL; + } + + /* stop transport channel timer */ + btu_stop_timer(&p_scb->timer_entry); + + if ((p_scb->role == AVDT_CLOSE_INT) || (p_scb->role == AVDT_OPEN_INT)) + { + /* tell ccb we're done with signaling channel */ + avdt_ccb_event(p_ccb, AVDT_CCB_UL_CLOSE_EVT, NULL); + } + event = (p_scb->role == AVDT_CLOSE_INT) ? AVDT_CLOSE_CFM_EVT : AVDT_CLOSE_IND_EVT; + p_scb->role = AVDT_CLOSE_ACP; + + if (p_scb->remove) + { + avdt_scb_dealloc(p_scb, NULL); + } + + /* call app callback */ + (*p_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_delay_rpt_req +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_delay_rpt_req (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT, (tAVDT_MSG *) &p_data->apidelay); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_delay_rpt_cmd +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_delay_rpt_cmd (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_DELAY_REPORT_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); + + if (p_scb->p_ccb) + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg); + else + avdt_scb_rej_not_in_use(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_hdl_delay_rpt_rsp +** +** Description This function calls the application callback with a delay +** report. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_delay_rpt_rsp (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_DELAY_REPORT_CFM_EVT, + (tAVDT_CTRL *) &p_data->msg.hdr); +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_close_sto +** +** Description This function is called when a channel is closed in OPEN +** state. Check the channel type and process accordingly. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + /* AVDT_CHAN_SIG does not visit this action */ + if(p_data && p_data->close.type != AVDT_CHAN_MEDIA) + { + /* it's reporting or recovery channel, + * the channel close in open state means the peer does not support it */ + if(p_data->close.old_tc_state == AVDT_AD_ST_OPEN) + { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = 0; + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_REPORT_DISCONN_EVT, &avdt_ctrl); + } + } + else + { + /* must be in OPEN state. need to go back to idle */ + avdt_scb_event(p_scb, AVDT_SCB_MSG_ABORT_RSP_EVT, NULL); + avdt_scb_hdl_tc_close(p_scb, p_data); + } +} +#endif + +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_open +** +** Description This function is called when the transport channel is +** opened while in the opening state. It calls the +** application callback with an open indication or open +** confirm depending on who initiated the open procedure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 event; +#if AVDT_REPORTING == TRUE + UINT8 role; +#endif + + /* stop transport channel connect timer */ + btu_stop_timer(&p_scb->timer_entry); + + event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT; + p_data->open.hdr.err_code = 0; + + AVDT_TRACE_DEBUG3("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x", + p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask, p_scb->curr_cfg.psc_mask); +#if AVDT_REPORTING == TRUE + if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) + { + /* open the reporting channel, if both devices support it */ + role = (p_scb->role == AVDT_OPEN_INT) ? AVDT_INT : AVDT_ACP; + avdt_ad_open_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, role); + } +#endif + + /* call app callback */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + event, + (tAVDT_CTRL *) &p_data->open); +} + +#if AVDT_REPORTING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_tc_open_sto +** +** Description This function is called when the transport channel is +** opened while in the opening state. It calls the +** application callback with an open indication or open +** confirm depending on who initiated the open procedure. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + /* open reporting channel here, when it is implemented */ + + /* call app callback */ + if(p_data->open.hdr.err_code == AVDT_CHAN_REPORT) + { + avdt_ctrl.hdr.err_code = 0; + avdt_ctrl.hdr.err_param = 1; + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL, + AVDT_REPORT_CONN_EVT, &avdt_ctrl); + } +} +#endif + +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req_no_frag +** +** Description This function frees the media packet currently stored in +** the SCB, if any. Then it builds a new media packet from +** with the passed in buffer and stores it in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p; + UINT32 ssrc; + + /* free packet we're holding, if any; to be replaced with new */ + if (p_scb->p_pkt != NULL) + { + GKI_freebuf(p_scb->p_pkt); + + /* this shouldn't be happening */ + AVDT_TRACE_WARNING0("Dropped media packet; congested"); + } + + /* build a media packet */ + + ssrc = avdt_scb_gen_ssrc(p_scb); + + p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE; + p_data->apiwrite.p_buf->offset -= AVDT_MEDIA_HDR_SIZE; + + p = (UINT8 *)(p_data->apiwrite.p_buf + 1) + p_data->apiwrite.p_buf->offset; + + UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1); + UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt); + UINT16_TO_BE_STREAM(p, p_scb->media_seq); + UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp); + UINT32_TO_BE_STREAM(p, ssrc); + + p_scb->media_seq++; + + /* store it */ + p_scb->p_pkt = p_data->apiwrite.p_buf; +} + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req_frag +** +** Description This function builds a new fragments of media packet from +** the passed in buffers and stores them in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + UINT8 *p; + UINT32 ssrc; + BT_HDR *p_frag; + + /* free fragments we're holding, if any; it shouldn't happen */ + if (!GKI_queue_is_empty(&p_scb->frag_q)) + { + while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) + GKI_freebuf(p_frag); + + /* this shouldn't be happening */ + AVDT_TRACE_WARNING0("*** Dropped media packet; congested"); + } + + /* build a media fragments */ + p_scb->frag_off = p_data->apiwrite.data_len; + p_scb->p_next_frag = p_data->apiwrite.p_data; + + ssrc = avdt_scb_gen_ssrc(p_scb); + + /* get first packet */ + p_frag = (BT_HDR*)GKI_getfirst (&p_data->apiwrite.frag_q); + /* posit on Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + + /* Adaptation Layer header */ + /* TSID, no-fragment bit and coding of length(in 2 length octets following) */ + *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | AVDT_ALH_LCODE_16BIT; + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific+AVDT_MEDIA_HDR_SIZE ); + /* media header */ + UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1); + UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt); + UINT16_TO_BE_STREAM(p, p_scb->media_seq); + UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp); + UINT32_TO_BE_STREAM(p, ssrc); + p_scb->media_seq++; + + while((p_frag = (BT_HDR*)GKI_getnext (p_frag)) != NULL) + { + /* posit on Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + /* Adaptation Layer header */ + /* TSID, fragment bit and coding of length(in 2 length octets following) */ + *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT); + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); + } + + /* store it */ + p_scb->frag_q = p_data->apiwrite.frag_q; +} +#endif + + +/******************************************************************************* +** +** Function avdt_scb_hdl_write_req +** +** Description This function calls one of the two versions of building functions +** for case with and without fragmentation +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_MULTIPLEXING == TRUE + if (GKI_queue_is_empty(&p_data->apiwrite.frag_q)) +#endif + avdt_scb_hdl_write_req_no_frag(p_scb, p_data); +#if AVDT_MULTIPLEXING == TRUE + else + avdt_scb_hdl_write_req_frag(p_scb, p_data); +#endif +} + +/******************************************************************************* +** +** Function avdt_scb_snd_abort_req +** +** Description This function sends an abort command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + + if (p_scb->p_ccb != NULL) + { + p_scb->role = AVDT_CLOSE_INT; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, (tAVDT_MSG *) &hdr); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_abort_rsp +** +** Description This function sends an abort response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT, + &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_close_req +** +** Description This function sends a close command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + + p_scb->role = AVDT_CLOSE_INT; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_stream_close +** +** Description This function sends a close command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_MULTIPLEXING == TRUE + BT_HDR *p_frag; + + AVDT_TRACE_WARNING2("avdt_scb_snd_stream_close c:%d, off:%d", + p_scb->frag_q.count, p_scb->frag_off); + /* clean fragments queue */ + while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) + GKI_freebuf(p_frag); + p_scb->frag_off = 0; +#endif + if (p_scb->p_pkt) + { + GKI_freebuf(p_scb->p_pkt); + p_scb->p_pkt = NULL; + } + +#if 0 + if(p_scb->cong) + p_scb->cong = FALSE; + + /* p_scb->curr_cfg.mux_tsid_media == 0 */ +#endif + avdt_scb_snd_close_req(p_scb, p_data); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_close_rsp +** +** Description This function sends a close response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_getconfig_req +** +** Description This function sends a get configuration command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_getconfig_rsp +** +** Description This function sends a get configuration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_open_req +** +** Description This function sends an open command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_EVT_HDR hdr; + + hdr.seid = p_scb->peer_seid; + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_open_rsp +** +** Description This function sends an open response message. It also +** calls avdt_ad_open_req() to accept a transport channel +** connection. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + /* notify adaption that we're waiting for transport channel open */ + p_scb->role = AVDT_OPEN_ACP; + avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP); + + /* send response */ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_OPEN, &p_data->msg); + + /* start tc connect timer */ + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_reconfig_req +** +** Description This function stores the configuration parameters in the +** SCB and sends a reconfiguration command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG)); + p_data->msg.hdr.seid = p_scb->peer_seid; + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_reconfig_rsp +** +** Description This function stores the configuration parameters in the +** SCB and sends a reconfiguration response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) + { + /* store new configuration */ + if (p_scb->req_cfg.num_codec > 0) + { + p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec; + memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE); + } + if (p_scb->req_cfg.num_protect > 0) + { + p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect; + memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE); + } + + /* send response */ + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); + } + else + { + /* send reject */ + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_security_req +** +** Description This function sends a security command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.hdr.seid = p_scb->peer_seid; + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_security_rsp +** +** Description This function sends a security response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_data->msg.hdr.err_code == 0) + { + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); + } + else + { + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_rej +** +** Description This function marks the SCB as not in use and sends a +** set configuration reject message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_scb->p_ccb != NULL) + { + avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); + + /* clear scb variables */ + avdt_scb_clr_vars(p_scb, p_data); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_req +** +** Description This function marks the SCB as in use and copies the +** configuration parameters to the SCB. Then the function +** sends a set configuration command message and initiates +** opening of the signaling channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CFG *p_req, *p_cfg; + + /* copy API parameters to scb, set scb as in use */ + p_scb->in_use = TRUE; + p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx); + p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid; + p_req = p_data->msg.config_cmd.p_cfg; + p_cfg = &p_scb->cs.cfg; +#if AVDT_MULTIPLEXING == TRUE + p_req->mux_tsid_media = p_cfg->mux_tsid_media; + p_req->mux_tcid_media = p_cfg->mux_tcid_media; + if(p_req->psc_mask & AVDT_PSC_REPORT) + { + p_req->mux_tsid_report = p_cfg->mux_tsid_report; + p_req->mux_tcid_report = p_cfg->mux_tcid_report; + } +#endif + memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG)); + + avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg); + + /* tell ccb to open channel */ + avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL); +} + +/******************************************************************************* +** +** Function avdt_scb_snd_setconfig_rsp +** +** Description This function copies the requested configuration into the +** current configuration and sends a set configuration +** response message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + if (p_scb->p_ccb != NULL) + { + memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG)); + + avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg); + } +} + +/******************************************************************************* +** +** Function avdt_scb_snd_tc_close +** +** Description This function calls avdt_ad_close_req() to close the +** transport channel for this SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ +#if AVDT_REPORTING == TRUE + if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) + avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb); +#endif + avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); +} + +/******************************************************************************* +** +** Function avdt_scb_cb_err +** +** Description This function calls the application callback function +** indicating an error. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + + /* call callback, using lookup table to get callback event */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), + NULL, + avdt_scb_cback_evt[p_scb->curr_evt], + &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_cong_state +** +** Description This function sets the congestion state of the SCB media +** transport channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->cong = p_data->llcong; +} + +/******************************************************************************* +** +** Function avdt_scb_rej_state +** +** Description This function sends a reject message to the peer indicating +** incorrect state for the received command message. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_rej_in_use +** +** Description This function sends a reject message to the peer indicating +** the stream is in use. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.hdr.err_code = AVDT_ERR_IN_USE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_rej_not_in_use +** +** Description This function sends a reject message to the peer indicating +** the stream is in use. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE; + p_data->msg.hdr.err_param = 0; + avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), + p_data->msg.hdr.sig_id, &p_data->msg); +} + +/******************************************************************************* +** +** Function avdt_scb_set_remove +** +** Description This function marks an SCB to be removed. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->remove = TRUE; +} + +/******************************************************************************* +** +** Function avdt_scb_free_pkt +** +** Description This function frees the media packet passed in. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; +#if AVDT_MULTIPLEXING == TRUE + BT_HDR *p_frag; +#endif + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + + /* p_buf can be NULL in case using of fragments queue frag_q */ + if(p_data->apiwrite.p_buf) + GKI_freebuf(p_data->apiwrite.p_buf); + +#if AVDT_MULTIPLEXING == TRUE + /* clean fragments queue */ + while((p_frag = (BT_HDR*)GKI_dequeue (&p_data->apiwrite.frag_q)) != NULL) + GKI_freebuf(p_frag); +#endif + + AVDT_TRACE_WARNING0("Dropped media packet"); + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); +} + +/******************************************************************************* +** +** Function avdt_scb_clr_pkt +** +** Description This function frees the media packet stored in the SCB. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + tAVDT_CCB *p_ccb; + UINT8 tcid; + UINT16 lcid; +#if AVDT_MULTIPLEXING == TRUE + BT_HDR *p_frag; +#endif + + /* set error code and parameter */ + avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE; + avdt_ctrl.hdr.err_param = 0; + /* flush the media data queued at L2CAP */ + if((p_ccb = p_scb->p_ccb) != NULL) + { + /* get tcid from type, scb */ + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid; + L2CA_FlushChannel (lcid, L2CAP_FLUSH_CHANS_ALL); + } + + if (p_scb->p_pkt != NULL) + { + GKI_freebuf(p_scb->p_pkt); + p_scb->p_pkt = NULL; + + AVDT_TRACE_DEBUG0("Dropped stored media packet"); + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); + } +#if AVDT_MULTIPLEXING == TRUE + else if(!GKI_queue_is_empty (&p_scb->frag_q)) + { + AVDT_TRACE_DEBUG0("Dropped fragments queue"); + /* clean fragments queue */ + while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) + GKI_freebuf(p_frag); + + p_scb->frag_off = 0; + + /* we need to call callback to keep data flow going */ + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, + &avdt_ctrl); + } +#endif +} + + +/******************************************************************************* +** +** Function avdt_scb_chk_snd_pkt +** +** Description This function checks if the SCB is congested, and if not +** congested it sends a stored media packet, if any. After it +** sends the packet it calls the application callback function +** with a write confirm. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + tAVDT_CTRL avdt_ctrl; + BT_HDR *p_pkt; +#if AVDT_MULTIPLEXING == TRUE + BOOLEAN sent = FALSE; + UINT8 res = AVDT_AD_SUCCESS; + tAVDT_SCB_EVT data; +#endif + + avdt_ctrl.hdr.err_code = 0; + + if (!p_scb->cong) + { + if (p_scb->p_pkt != NULL) + { + p_pkt = p_scb->p_pkt; + p_scb->p_pkt = NULL; + avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); + + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); + } +#if AVDT_MULTIPLEXING == TRUE + else + { +#if 0 + AVDT_TRACE_DEBUG1("num_q=%d", + L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid), + L2CAP_FLUSH_CHANS_GET); +#endif + while((p_pkt = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL) + { + sent = TRUE; + AVDT_TRACE_DEBUG1("Send fragment len=%d",p_pkt->len); + /* fragments queue contains fragment to send */ + res = avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt); + if(AVDT_AD_CONGESTED == res) + { + p_scb->cong = TRUE; + AVDT_TRACE_DEBUG0("avdt/l2c congested!!"); + break;/* exit loop if channel became congested */ + } + } + AVDT_TRACE_DEBUG2("res=%d left=%d",res, p_scb->frag_off); + + if(p_scb->frag_off) + { + if(AVDT_AD_SUCCESS == res || GKI_queue_is_empty (&p_scb->frag_q)) + { + /* all buffers were sent to L2CAP, compose more to queue */ + avdt_scb_queue_frags(p_scb, &p_scb->p_next_frag, &p_scb->frag_off, &p_scb->frag_q); + if(!GKI_queue_is_empty (&p_scb->frag_q)) + { + data.llcong = p_scb->cong; + avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &data); + } + } + } + + /* Send event AVDT_WRITE_CFM_EVT if it was last fragment */ + else if (sent && GKI_queue_is_empty (&p_scb->frag_q)) + { + (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl); + } + } +#endif + } +} + +/******************************************************************************* +** +** Function avdt_scb_tc_timer +** +** Description This function is called to start a timer when the peer +** initiates closing of the stream. The timer verifies that +** the peer disconnects the transport channel. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_DISC_TOUT); +} + +/******************************************************************************* +** +** Function avdt_scb_clr_vars +** +** Description This function initializes certain SCB variables. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) +{ + p_scb->in_use = FALSE; + p_scb->p_ccb = NULL; + p_scb->peer_seid = 0; +} + +#if AVDT_MULTIPLEXING == TRUE +/******************************************************************************* +** +** Function avdt_scb_queue_frags +** +** Description This function breaks media payload into fragments +** and put the fragments in the given queue. +** +** Returns Nothing. +** +*******************************************************************************/ +void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, BUFFER_Q *pq) +{ + UINT16 lcid; + UINT16 num_frag; + UINT16 mtu_used; + UINT8 *p; + BOOLEAN al_hdr = FALSE; + UINT8 tcid; + tAVDT_TC_TBL *p_tbl; + UINT16 buf_size; + UINT16 offset = AVDT_MEDIA_OFFSET + AVDT_AL_HDR_SIZE; + UINT16 cont_offset = offset - AVDT_MEDIA_HDR_SIZE; + BT_HDR *p_frag; + + tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb); + lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][tcid].lcid; + + if( p_scb->frag_off != 0) + { + /* continuing process is usually triggered by un-congest event. + * the number of buffers at L2CAP is very small (if not 0). + * we do not need to L2CA_FlushChannel() */ + offset = cont_offset; + al_hdr = TRUE; + num_frag = AVDT_MAX_FRAG_COUNT; + } + else + { + num_frag = L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_GET); + AVDT_TRACE_DEBUG2("num_q=%d lcid=%d", num_frag, lcid); + if(num_frag >= AVDT_MAX_FRAG_COUNT) + { + num_frag = 0; + } + else + { + num_frag = AVDT_MAX_FRAG_COUNT - num_frag; + } + } + + /* look up transport channel table entry to get peer mtu */ + p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb); + buf_size = p_tbl->peer_mtu + BT_HDR_SIZE; + AVDT_TRACE_DEBUG3("peer_mtu: %d, buf_size: %d num_frag=%d", + p_tbl->peer_mtu, buf_size, num_frag); + + if(buf_size > AVDT_DATA_POOL_SIZE) + buf_size = AVDT_DATA_POOL_SIZE; + + mtu_used = buf_size - BT_HDR_SIZE; + + while(*p_data_len && num_frag) + { + /* allocate buffer for fragment */ + if(NULL == (p_frag = (BT_HDR*)GKI_getbuf(buf_size))) + { + AVDT_TRACE_WARNING1("avdt_scb_queue_frags len=%d(out of GKI buffers)",*p_data_len); + break; + } + /* fill fragment by chunk of media payload */ + p_frag->layer_specific = *p_data_len;/* length of all remaining transport packet */ + p_frag->offset = offset; + /* adjust packet offset for continuing packets */ + offset = cont_offset; + + p_frag->len = mtu_used - p_frag->offset; + if(p_frag->len > *p_data_len) + p_frag->len = *p_data_len; + memcpy((UINT8*)(p_frag+1) + p_frag->offset, *pp_data, p_frag->len); + *pp_data += p_frag->len; + *p_data_len -= p_frag->len; + AVDT_TRACE_DEBUG1("Prepared fragment len=%d", p_frag->len); + + if(al_hdr) + { + /* Adaptation Layer header */ + p_frag->len += AVDT_AL_HDR_SIZE; + p_frag->offset -= AVDT_AL_HDR_SIZE; + p = (UINT8 *)(p_frag + 1) + p_frag->offset; + /* TSID, fragment bit and coding of length(in 2 length octets following) */ + *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT); + + /* length of all remaining transport packet */ + UINT16_TO_BE_STREAM(p, p_frag->layer_specific ); + } + /* put fragment into gueue */ + GKI_enqueue(pq, p_frag); + num_frag--; + } +} +#endif + diff --git a/stack/avrc/avrc_api.c b/stack/avrc/avrc_api.c new file mode 100644 index 0000000..b6347d7 --- /dev/null +++ b/stack/avrc/avrc_api.c @@ -0,0 +1,588 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to AVRCP mandatory commands + * + ******************************************************************************/ +#include + +#include "gki.h" +#include "avrc_api.h" +#include "avrc_int.h" +#include "wcassert.h" + +/***************************************************************************** +** Global data +*****************************************************************************/ + + +#define AVRC_MAX_RCV_CTRL_EVT AVCT_BROWSE_UNCONG_IND_EVT + +static const UINT8 avrc_ctrl_event_map[] = +{ + AVRC_OPEN_IND_EVT, /* AVCT_CONNECT_CFM_EVT */ + AVRC_OPEN_IND_EVT, /* AVCT_CONNECT_IND_EVT */ + AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_CFM_EVT */ + AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_IND_EVT */ + AVRC_CONG_IND_EVT, /* AVCT_CONG_IND_EVT */ + AVRC_UNCONG_IND_EVT,/* AVCT_UNCONG_IND_EVT */ + AVRC_BROWSE_OPEN_IND_EVT, /* AVCT_BROWSE_CONN_CFM_EVT */ + AVRC_BROWSE_OPEN_IND_EVT, /* AVCT_BROWSE_CONN_IND_EVT */ + AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_CFM_EVT */ + AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_IND_EVT */ + AVRC_BROWSE_CONG_IND_EVT, /* AVCT_BROWSE_CONG_IND_EVT */ + AVRC_BROWSE_UNCONG_IND_EVT /* AVCT_BROWSE_UNCONG_IND_EVT */ +}; + +#define AVRC_OP_DROP 0xFE /* use this unused opcode to indication no need to call the callback function */ +#define AVRC_OP_DROP_N_FREE 0xFD /* use this unused opcode to indication no need to call the callback function & free buffer */ + +/****************************************************************************** +** +** Function avrc_ctrl_cback +** +** Description This is the callback function used by AVCTP to report +** received link events. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_ctrl_cback(UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr) +{ + UINT8 avrc_event; + + if (event <= AVRC_MAX_RCV_CTRL_EVT && avrc_cb.ccb[handle].p_ctrl_cback) + { + avrc_event = avrc_ctrl_event_map[event]; + if (event == AVCT_CONNECT_CFM_EVT) + { + if (result != 0) /* failed */ + avrc_event = AVRC_CLOSE_IND_EVT; + } + (*avrc_cb.ccb[handle].p_ctrl_cback)(handle, avrc_event, result, peer_addr); + } + /* else drop the unknown event*/ +} + +/****************************************************************************** +** +** Function avrc_get_data_ptr +** +** Description If the offset in the received buffer is smaller than required +** move the portion of data AVRC cares. +** +** Returns Nothing. +** +******************************************************************************/ +static UINT8 * avrc_get_data_ptr(BT_HDR *p_pkt) +{ + UINT8 *p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + int i, gap; + + if (p_pkt->offset < AVCT_MSG_OFFSET) + { + gap = AVCT_MSG_OFFSET - p_pkt->offset; + for(i=p_pkt->len; i>0; i--) + { + *(p_data + i + gap) = *(p_data + i); + } + p_pkt->offset += gap; + p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + } + *p_data = AVRC_RSP_IMPL_STBL; + return p_data; +} + + +/****************************************************************************** +** +** Function avrc_msg_cback +** +** Description This is the callback function used by AVCTP to report +** received AV control messages. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr, + BT_HDR *p_pkt) +{ + UINT8 opcode; + tAVRC_MSG msg; + UINT8 *p_data; + UINT8 *p_begin; + BOOLEAN drop = FALSE; + BOOLEAN free = TRUE; + BT_HDR *p_rsp = NULL; + UINT8 *p_rsp_data; + int xx; + BOOLEAN reject = FALSE; +#if (BT_USE_TRACES == TRUE) + char *p_drop_msg = "dropped"; +#endif + tAVRC_MSG_VENDOR *p_msg = &msg.vendor; + + if (cr == AVCT_CMD && + (p_pkt->layer_specific & AVCT_DATA_CTRL && AVRC_PACKET_LEN < sizeof(p_pkt->len))) + { + /* Ignore the invalid AV/C command frame */ +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "dropped - too long AV/C cmd frame size"; +#endif + GKI_freebuf(p_pkt); + return; + } + + if (cr == AVCT_REJ) + { + /* The peer thinks that this PID is no longer open - remove this handle */ + /* */ + GKI_freebuf(p_pkt); + AVCT_RemoveConn(handle); + return; + } + + p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + memset(&msg, 0, sizeof(tAVRC_MSG) ); + { + msg.hdr.ctype = p_data[0] & AVRC_CTYPE_MASK; + AVRC_TRACE_DEBUG4("avrc_msg_cback handle:%d, ctype:%d, offset:%d, len: %d", + handle, msg.hdr.ctype, p_pkt->offset, p_pkt->len); + msg.hdr.subunit_type = (p_data[1] & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.hdr.subunit_id = p_data[1] & AVRC_SUBID_MASK; + opcode = p_data[2]; + } + + if ( ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) || + ((avrc_cb.ccb[handle].control & AVRC_CT_CONTROL) && (cr == AVCT_RSP)) ) + { + + switch(opcode) + { + case AVRC_OP_UNIT_INFO: + if (cr == AVCT_CMD) + { + /* send the response to the peer */ + p_rsp = p_pkt; /* this also sets free = FALSE, drop = TRUE */ + /* check & set the offset. set response code, set subunit_type & subunit_id, + set AVRC_OP_UNIT_INFO */ + p_rsp_data = avrc_get_data_ptr(p_pkt) + AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */ + *p_rsp_data++ = 7; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + AVRC_CO_ID_TO_BE_STREAM(p_rsp_data, avrc_cb.ccb[handle].company_id); + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "auto respond"; +#endif + } + else + { + /* parse response */ + p_data += 4; /* 3 bytes: ctype, subunit*, opcode + octet 3 (is 7)*/ + msg.unit.unit_type = (*p_data & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.unit.unit = *p_data & AVRC_SUBID_MASK; + p_data++; + AVRC_BE_STREAM_TO_CO_ID(msg.unit.company_id, p_data); + } + break; + + case AVRC_OP_SUB_INFO: + if (cr == AVCT_CMD) + { + /* send the response to the peer */ + p_rsp = p_pkt; /* this also sets free = FALSE, drop = TRUE */ + /* check & set the offset. set response code, set (subunit_type & subunit_id), + set AVRC_OP_SUB_INFO, set (page & extention code) */ + p_rsp_data = avrc_get_data_ptr(p_pkt) + 4; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + memset(p_rsp_data, AVRC_CMD_OPRND_PAD, AVRC_SUBRSP_OPRND_BYTES); + p_rsp_data += AVRC_SUBRSP_OPRND_BYTES; + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "auto responded"; +#endif + } + else + { + /* parse response */ + p_data += AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */ + msg.sub.page = (*p_data++ >> AVRC_SUB_PAGE_SHIFT) & AVRC_SUB_PAGE_MASK; + xx = 0; + while (*p_data != AVRC_CMD_OPRND_PAD && xx> AVRC_SUBTYPE_SHIFT; + if (msg.sub.subunit_type[xx] == AVRC_SUB_PANEL) + msg.sub.panel = TRUE; + xx++; + } + } + break; + + case AVRC_OP_VENDOR: + p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + p_begin = p_data; + if (p_pkt->len < AVRC_VENDOR_HDR_SIZE) /* 6 = ctype, subunit*, opcode & CO_ID */ + { + if (cr == AVCT_CMD) + reject = TRUE; + else + drop = TRUE; + break; + } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + AVRC_BE_STREAM_TO_CO_ID(p_msg->company_id, p_data); + p_msg->p_vendor_data = p_data; + p_msg->vendor_len = p_pkt->len - (p_data - p_begin); + + break; + + case AVRC_OP_PASS_THRU: + if (p_pkt->len < 5) /* 3 bytes: ctype, subunit*, opcode & op_id & len */ + { + if (cr == AVCT_CMD) + reject = TRUE; + else + drop = TRUE; + break; + } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + msg.pass.op_id = (AVRC_PASS_OP_ID_MASK & *p_data); + if (AVRC_PASS_STATE_MASK & *p_data) + msg.pass.state = TRUE; + else + msg.pass.state = FALSE; + p_data++; + msg.pass.pass_len = *p_data++; + if (msg.pass.pass_len != p_pkt->len - 5) + msg.pass.pass_len = p_pkt->len - 5; + if (msg.pass.pass_len) + msg.pass.p_pass_data = p_data; + else + msg.pass.p_pass_data = NULL; + break; + + + default: + if ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) + { + /* reject unsupported opcode */ + reject = TRUE; + } + drop = TRUE; + break; + } + } + else /* drop the event */ + { + drop = TRUE; + } + + if (reject) + { + /* reject unsupported opcode */ + p_rsp = p_pkt; /* this also sets free = FALSE, drop = TRUE */ + p_rsp_data = avrc_get_data_ptr(p_pkt); + *p_rsp_data = AVRC_RSP_REJ; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "rejected"; +#endif + cr = AVCT_RSP; + drop = TRUE; + } + + if (p_rsp) + { + /* set to send response right away */ + AVCT_MsgReq( handle, label, cr, p_rsp); + free = FALSE; + drop = TRUE; + } + + if (drop == FALSE) + { + msg.hdr.opcode = opcode; + (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg); + } +#if (BT_USE_TRACES == TRUE) + else + { + AVRC_TRACE_WARNING5("avrc_msg_cback %s msg handle:%d, control:%d, cr:%d, opcode:x%x", + p_drop_msg, + handle, avrc_cb.ccb[handle].control, cr, opcode); + } +#endif + + + if (free) + GKI_freebuf(p_pkt); +} + + + + +/****************************************************************************** +** +** Function avrc_pass_msg +** +** Description Compose a PASS THROUGH command according to p_msg +** +** Input Parameters: +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns pointer to a valid GKI buffer if successful. +** NULL if p_msg is NULL. +** +******************************************************************************/ +static BT_HDR * avrc_pass_msg(tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_cmd = NULL; + UINT8 *p_data; + + WC_ASSERT(p_msg != NULL); + WC_ASSERT(AVRC_CMD_POOL_SIZE > (AVRC_MIN_CMD_LEN+p_msg->pass_len)); + + if ((p_cmd = (BT_HDR *) GKI_getpoolbuf(AVRC_CMD_POOL_ID)) != NULL) + { + p_cmd->offset = AVCT_MSG_OFFSET; + p_cmd->layer_specific = AVCT_DATA_CTRL; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = (p_msg->hdr.ctype & AVRC_CTYPE_MASK); + *p_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); /* Panel subunit & id=0 */ + *p_data++ = AVRC_OP_PASS_THRU; + *p_data = (AVRC_PASS_OP_ID_MASK&p_msg->op_id); + if (p_msg->state) + *p_data |= AVRC_PASS_STATE_MASK; + p_data++; + + if (p_msg->op_id == AVRC_ID_VENDOR) + { + *p_data++ = p_msg->pass_len; + if (p_msg->pass_len && p_msg->p_pass_data) + { + memcpy(p_data, p_msg->p_pass_data, p_msg->pass_len); + p_data += p_msg->pass_len; + } + } + else /* set msg len to 0 for other op_id */ + { + /* set msg len to 0 for other op_id */ + *p_data++ = 0; + } + p_cmd->len = (UINT16) (p_data - (UINT8 *)(p_cmd + 1) - p_cmd->offset); + } + return p_cmd; +} + +/****************************************************************************** +** +** Function AVRC_Open +** +** Description This function is called to open a connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the p_ccb->stream parameter. +** The connection can be a target, a controller or for both role, +** as determined by the p_ccb->control parameter. +** By definition, a target connection is an acceptor connection +** that waits for an incoming AVCTP connection from the peer. +** The connection remains available to the application until +** the application closes it by calling AVRC_Close(). The +** application does not need to reopen the connection after an +** AVRC_CLOSE_IND_EVT is received. +** +** Input Parameters: +** p_ccb->company_id: Company Identifier. +** +** p_ccb->p_ctrl_cback: Pointer to control callback function. +** +** p_ccb->p_msg_cback: Pointer to message callback function. +** +** p_ccb->conn: AVCTP connection role. This is set to +** AVCTP_INT for initiator connections and AVCTP_ACP +** for acceptor connections. +** +** p_ccb->control: Control role. This is set to +** AVRC_CT_TARGET for target connections, AVRC_CT_CONTROL +** for control connections or (AVRC_CT_TARGET|AVRC_CT_CONTROL) +** for connections that support both roles. +** +** peer_addr: BD address of peer device. This value is +** only used for initiator connections; for acceptor +** connections it can be set to NULL. +** +** Output Parameters: +** p_handle: Pointer to handle. This parameter is only +** valid if AVRC_SUCCESS is returned. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +UINT16 AVRC_Open(UINT8 *p_handle, tAVRC_CONN_CB *p_ccb, BD_ADDR_PTR peer_addr) +{ + UINT16 status; + tAVCT_CC cc; + + cc.p_ctrl_cback = avrc_ctrl_cback; /* Control callback */ + cc.p_msg_cback = avrc_msg_cback; /* Message callback */ + cc.pid = UUID_SERVCLASS_AV_REMOTE_CONTROL; /* Profile ID */ + cc.role = p_ccb->conn; /* Initiator/acceptor role */ + cc.control = p_ccb->control; /* Control role (Control/Target) */ + + status = AVCT_CreateConn(p_handle, &cc, peer_addr); + if (status == AVCT_SUCCESS) + { + memcpy(&avrc_cb.ccb[*p_handle], p_ccb, sizeof(tAVRC_CONN_CB)); + } + AVRC_TRACE_DEBUG4("AVRC_Open role: %d, control:%d status:%d, handle:%d", cc.role, cc.control, status, *p_handle); + + return status; +} + +/****************************************************************************** +** +** Function AVRC_Close +** +** Description Close a connection opened with AVRC_Open(). +** This function is called when the +** application is no longer using a connection. +** +** Input Parameters: +** handle: Handle of this connection. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_Close(UINT8 handle) +{ + AVRC_TRACE_DEBUG1("AVRC_Close handle:%d", handle); + return AVCT_RemoveConn(handle); +} + + +/****************************************************************************** +** +** Function AVRC_MsgReq +** +** Description This function is used to send the AVRCP byte stream in p_pkt +** down to AVCTP. +** +** It is expected that p_pkt->offset is at least AVCT_MSG_OFFSET +** p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE +** p_pkt->event is AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSE +** The above BT_HDR settings are set by the AVRC_Bld* functions. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt) +{ + return AVRC_NO_RESOURCES; +} + + +/****************************************************************************** +** +** Function AVRC_PassCmd +** +** Description Send a PASS THROUGH command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_PassCmd(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_buf; + WC_ASSERT(p_msg != NULL); + if (p_msg) + { + p_msg->hdr.ctype = AVRC_CMD_CTRL; + p_buf = avrc_pass_msg(p_msg); + if (p_buf) + return AVCT_MsgReq( handle, label, AVCT_CMD, p_buf); + } + return AVRC_NO_RESOURCES; +} + +/****************************************************************************** +** +** Function AVRC_PassRsp +** +** Description Send a PASS THROUGH response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a PASS THROUGH command +** message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_PassRsp(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg) +{ + BT_HDR *p_buf; + WC_ASSERT(p_msg != NULL); + if (p_msg) + { + p_buf = avrc_pass_msg(p_msg); + if (p_buf) + return AVCT_MsgReq( handle, label, AVCT_RSP, p_buf); + } + return AVRC_NO_RESOURCES; +} + diff --git a/stack/avrc/avrc_int.h b/stack/avrc/avrc_int.h new file mode 100644 index 0000000..b6c90b1 --- /dev/null +++ b/stack/avrc/avrc_int.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * VRCP internal header file. + * + ******************************************************************************/ + + +#ifndef AVRC_INT_H +#define AVRC_INT_H + +#include "avct_defs.h" +#include "avrc_api.h" + +/* DEBUG FLAGS + * + * #define META_DEBUG_ENABLED + */ +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Number of attributes in AVRC SDP record. */ +#define AVRC_NUM_ATTR 6 + +/* Number of protocol elements in protocol element list. */ +#define AVRC_NUM_PROTO_ELEMS 2 + +#ifndef AVRC_MIN_CMD_LEN +#define AVRC_MIN_CMD_LEN 20 +#endif + +#define AVRC_UNIT_OPRND_BYTES 5 +#define AVRC_SUB_OPRND_BYTES 4 +#define AVRC_SUBRSP_OPRND_BYTES 3 +#define AVRC_SUB_PAGE_MASK 7 +#define AVRC_SUB_PAGE_SHIFT 4 +#define AVRC_SUB_EXT_CODE 7 +#define AVRC_PASS_OP_ID_MASK 0x7F +#define AVRC_PASS_STATE_MASK 0x80 +#define AVRC_CMD_OPRND_PAD 0xFF + +#define AVRC_CTYPE_MASK 0x0F +#define AVRC_SUBTYPE_MASK 0xF8 +#define AVRC_SUBTYPE_SHIFT 3 +#define AVRC_SUBID_MASK 0x07 +#define AVRC_SUBID_IGNORE 0x07 + +#define AVRC_SINGLE_PARAM_SIZE 1 +#define AVRC_METADATA_PKT_TYPE_MASK 0x03 +#define AVRC_PASS_THOUGH_MSG_MASK 0x80 /* MSB of msg_type indicates the PAS THROUGH msg */ +#define AVRC_VENDOR_UNIQUE_MASK 0x70 /* vendor unique id */ + + +/* Company ID is 24-bit integer We can not use the macros in bt_types.h */ +#define AVRC_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define AVRC_BE_STREAM_TO_CO_ID(u32, p) {u32 = (((UINT32)(*((p) + 2))) + (((UINT32)(*((p) + 1))) << 8) + (((UINT32)(*(p))) << 16)); (p) += 3;} + +#define AVRC_AVC_HDR_SIZE 3 /* ctype, subunit*, opcode */ + +#define AVRC_MIN_META_HDR_SIZE 4 /* pdu id(1), packet type(1), param len(2) */ +#define AVRC_MIN_BROWSE_HDR_SIZE 3 /* pdu id(1), param len(2) */ + +#define AVRC_VENDOR_HDR_SIZE 6 /* ctype, subunit*, opcode, CO_ID */ +#define AVRC_MSG_VENDOR_OFFSET 23 +#define AVRC_MIN_VENDOR_SIZE (AVRC_MSG_VENDOR_OFFSET + BT_HDR_SIZE + AVRC_MIN_META_HDR_SIZE) + +#define AVRC_PASS_THRU_SIZE 8 +#define AVRC_MSG_PASS_THRU_OFFSET 25 +#define AVRC_MIN_PASS_THRU_SIZE (AVRC_MSG_PASS_THRU_OFFSET + BT_HDR_SIZE + 4) + +#define AVRC_MIN_BROWSE_SIZE (AVCT_BROWSE_OFFSET + BT_HDR_SIZE + AVRC_MIN_BROWSE_HDR_SIZE) + +#define AVRC_CTRL_PKT_LEN(pf, pk) {pf = (UINT8 *)((pk) + 1) + (pk)->offset + 2;} + +#define AVRC_MAX_CTRL_DATA_LEN (AVRC_PACKET_LEN) + +/***************************************************************************** +** Type definitions +*****************************************************************************/ + + +typedef struct +{ + tAVRC_CONN_CB ccb[AVCT_NUM_CONN]; + tAVRC_FIND_CBACK *p_cback; /* pointer to application callback */ + tSDP_DISCOVERY_DB *p_db; /* pointer to discovery database */ + UINT16 service_uuid; /* service UUID to search */ + UINT8 trace_level; +} tAVRC_CB; + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == FALSE +AVRC_API extern tAVRC_CB avrc_cb; +#else +AVRC_API extern tAVRC_CB *avrc_cb_ptr; +#define avrc_cb (*avrc_cb_ptr) +#endif + +extern BOOLEAN avrc_is_valid_pdu_id(UINT8 pdu_id); +extern BOOLEAN avrc_is_valid_player_attrib_value(UINT8 attrib, UINT8 value); +extern BT_HDR * avrc_alloc_ctrl_pkt (UINT8 pdu); +extern tAVRC_STS avrc_pars_pass_thru(tAVRC_MSG_PASS *p_msg, UINT16 *p_vendor_unique_id); +extern UINT8 avrc_opcode_from_pdu(UINT8 pdu); +extern BOOLEAN avrc_is_valid_opcode(UINT8 opcode); + +#ifdef __cplusplus +} +#endif + +#endif /* AVRC_INT_H */ + diff --git a/stack/avrc/avrc_opt.c b/stack/avrc/avrc_opt.c new file mode 100644 index 0000000..208a6c2 --- /dev/null +++ b/stack/avrc/avrc_opt.c @@ -0,0 +1,230 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to AVRCP optional commands + * + ******************************************************************************/ +#include + +#include "gki.h" +#include "avrc_api.h" +#include "avrc_int.h" + +#include "wcassert.h" + + +/****************************************************************************** +** +** Function avrc_vendor_msg +** +** Description Compose a VENDOR DEPENDENT command according to p_msg +** +** Input Parameters: +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns pointer to a valid GKI buffer if successful. +** NULL if p_msg is NULL. +** +******************************************************************************/ +static BT_HDR * avrc_vendor_msg(tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + WC_ASSERT(p_msg != NULL); + + WC_ASSERT(AVRC_CMD_POOL_SIZE > (AVRC_MIN_CMD_LEN+p_msg->vendor_len)); + if ((p_cmd = (BT_HDR *) GKI_getpoolbuf(AVRC_CMD_POOL_ID)) != NULL) + { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = (p_msg->hdr.ctype & AVRC_CTYPE_MASK); + *p_data++ = (p_msg->hdr.subunit_type << AVRC_SUBTYPE_SHIFT) | p_msg->hdr.subunit_id; + *p_data++ = AVRC_OP_VENDOR; + AVRC_CO_ID_TO_BE_STREAM(p_data, p_msg->company_id); + if(p_msg->vendor_len && p_msg->p_vendor_data) + { + memcpy(p_data, p_msg->p_vendor_data, p_msg->vendor_len); + } + p_cmd->len = (UINT16) (p_data + p_msg->vendor_len - (UINT8 *)(p_cmd + 1) - p_cmd->offset); + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return p_cmd; +} + +/****************************************************************************** +** +** Function AVRC_UnitCmd +** +** Description Send a UNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_UnitCmd(UINT8 handle, UINT8 label) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + if ((p_cmd = (BT_HDR *) GKI_getpoolbuf(AVRC_CMD_POOL_ID)) != NULL) + { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = AVRC_CMD_STATUS; + /* unit & id ignore */ + *p_data++ = (AVRC_SUB_UNIT << AVRC_SUBTYPE_SHIFT) | AVRC_SUBID_IGNORE; + *p_data++ = AVRC_OP_UNIT_INFO; + memset(p_data, AVRC_CMD_OPRND_PAD, AVRC_UNIT_OPRND_BYTES); + p_cmd->len = p_data + AVRC_UNIT_OPRND_BYTES - (UINT8 *)(p_cmd + 1) - p_cmd->offset; + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return AVCT_MsgReq( handle, label, AVCT_CMD, p_cmd); +} + +/****************************************************************************** +** +** Function AVRC_SubCmd +** +** Description Send a SUBUNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** page: Specifies which part of the subunit type table +** is requested. For AVRCP it is typically zero. +** Value range is 0-7. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_SubCmd(UINT8 handle, UINT8 label, UINT8 page) +{ + BT_HDR *p_cmd; + UINT8 *p_data; + + if ((p_cmd = (BT_HDR *) GKI_getpoolbuf(AVRC_CMD_POOL_ID)) != NULL) + { + p_cmd->offset = AVCT_MSG_OFFSET; + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + *p_data++ = AVRC_CMD_STATUS; + /* unit & id ignore */ + *p_data++ = (AVRC_SUB_UNIT << AVRC_SUBTYPE_SHIFT) | AVRC_SUBID_IGNORE; + *p_data++ = AVRC_OP_SUB_INFO; + *p_data++ = ((page&AVRC_SUB_PAGE_MASK) << AVRC_SUB_PAGE_SHIFT) | AVRC_SUB_EXT_CODE; + memset(p_data, AVRC_CMD_OPRND_PAD, AVRC_SUB_OPRND_BYTES); + p_cmd->len = p_data + AVRC_SUB_OPRND_BYTES - (UINT8 *)(p_cmd + 1) - p_cmd->offset; + p_cmd->layer_specific = AVCT_DATA_CTRL; + } + return AVCT_MsgReq( handle, label, AVCT_CMD, p_cmd); +} + +/****************************************************************************** +** +** Function AVRC_VendorCmd +** +** Description Send a VENDOR DEPENDENT command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_VendorCmd(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_buf = avrc_vendor_msg(p_msg); + if (p_buf) + return AVCT_MsgReq( handle, label, AVCT_CMD, p_buf); + else + return AVCT_NO_RESOURCES; +} + + +/****************************************************************************** +** +** Function AVRC_VendorRsp +** +** Description Send a VENDOR DEPENDENT response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a VENDOR DEPENDENT +** command message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +UINT16 AVRC_VendorRsp(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg) +{ + BT_HDR *p_buf = avrc_vendor_msg(p_msg); + if (p_buf) + return AVCT_MsgReq( handle, label, AVCT_RSP, p_buf); + else + return AVCT_NO_RESOURCES; +} + + + diff --git a/stack/avrc/avrc_sdp.c b/stack/avrc/avrc_sdp.c new file mode 100644 index 0000000..427894c --- /dev/null +++ b/stack/avrc/avrc_sdp.c @@ -0,0 +1,297 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * AVRCP SDP related functions + * + ******************************************************************************/ +#include + +#include "gki.h" +#include "avrc_api.h" +#include "avrc_int.h" + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if AVRC_DYNAMIC_MEMORY == FALSE +tAVRC_CB avrc_cb; +#endif + +/* update AVRC_NUM_PROTO_ELEMS if this constant is changed */ +const tSDP_PROTOCOL_ELEM avrc_proto_list [] = +{ + {UUID_PROTOCOL_L2CAP, 1, {AVCT_PSM, 0} }, + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_0, 0} } +}; + + + +/****************************************************************************** +** +** Function avrc_sdp_cback +** +** Description This is the SDP callback function used by A2D_FindService. +** This function will be executed by SDP when the service +** search is completed. If the search is successful, it +** finds the first record in the database that matches the +** UUID of the search. Then retrieves various parameters +** from the record. When it is finished it calls the +** application callback function. +** +** Returns Nothing. +** +******************************************************************************/ +static void avrc_sdp_cback(UINT16 status) +{ + AVRC_TRACE_API1("avrc_sdp_cback status: %d", status); + + /* reset service_uuid, so can start another find service */ + avrc_cb.service_uuid = 0; + + /* return info from sdp record in app callback function */ + (*avrc_cb.p_cback) (status); + + return; +} + +/****************************************************************************** +** +** Function AVRC_FindService +** +** Description This function is called by the application to perform service +** discovery and retrieve AVRCP SDP record information from a +** peer device. Information is returned for the first service +** record found on the server that matches the service UUID. +** The callback function will be executed when service discovery +** is complete. There can only be one outstanding call to +** AVRC_FindService() at a time; the application must wait for +** the callback before it makes another call to the function. +** The application is responsible for allocating memory for the +** discovery database. It is recommended that the size of the +** discovery database be at least 300 bytes. The application +** can deallocate the memory after the callback function has +** executed. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** bd_addr: BD address of the peer device. +** +** p_db: SDP discovery database parameters. +** +** p_cback: Pointer to the callback function. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_PARAMS if discovery database parameters are invalid. +** AVRC_NO_RESOURCES if there are not enough resources to +** perform the service search. +** +******************************************************************************/ +UINT16 AVRC_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tAVRC_SDP_DB_PARAMS *p_db, tAVRC_FIND_CBACK *p_cback) +{ + tSDP_UUID uuid_list; + BOOLEAN result = TRUE; + UINT16 a2d_attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST, /* update AVRC_NUM_ATTR, if changed */ + ATTR_ID_PROTOCOL_DESC_LIST, + ATTR_ID_BT_PROFILE_DESC_LIST, + ATTR_ID_SERVICE_NAME, + ATTR_ID_SUPPORTED_FEATURES, + ATTR_ID_PROVIDER_NAME}; + + AVRC_TRACE_API1("AVRC_FindService uuid: %x", service_uuid); + if( (service_uuid != UUID_SERVCLASS_AV_REM_CTRL_TARGET && service_uuid != UUID_SERVCLASS_AV_REMOTE_CONTROL) || + p_db == NULL || p_db->p_db == NULL || p_cback == NULL) + return AVRC_BAD_PARAM; + + /* check if it is busy */ + if( avrc_cb.service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET || + avrc_cb.service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) + return AVRC_NO_RESOURCES; + + /* set up discovery database */ + uuid_list.len = LEN_UUID_16; + uuid_list.uu.uuid16 = service_uuid; + + if(p_db->p_attrs == NULL || p_db->num_attr == 0) + { + p_db->p_attrs = a2d_attr_list; + p_db->num_attr = AVRC_NUM_ATTR; + } + + result = SDP_InitDiscoveryDb(p_db->p_db, p_db->db_len, 1, &uuid_list, p_db->num_attr, + p_db->p_attrs); + + if (result == TRUE) + { + /* store service_uuid and discovery db pointer */ + avrc_cb.p_db = p_db->p_db; + avrc_cb.service_uuid = service_uuid; + avrc_cb.p_cback = p_cback; + + /* perform service search */ + result = SDP_ServiceSearchAttributeRequest(bd_addr, p_db->p_db, avrc_sdp_cback); + } + + return (result ? AVRC_SUCCESS : AVRC_FAIL); +} + +/****************************************************************************** +** +** Function AVRC_AddRecord +** +** Description This function is called to build an AVRCP SDP record. +** Prior to calling this function the application must +** call SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** If service name is not used set this to NULL. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** If provider name is not used set this to NULL. +** +** categories: Supported categories. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if not enough resources to build the SDP record. +** +******************************************************************************/ +UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, + char *p_provider_name, UINT16 categories, UINT32 sdp_handle) +{ + UINT16 browse_list[1]; + BOOLEAN result = TRUE; + UINT8 temp[8]; + UINT8 *p; + UINT16 count = 1; + UINT16 class_list[2]; + + + AVRC_TRACE_API1("AVRC_AddRecord uuid: %x", service_uuid); + + if( service_uuid != UUID_SERVCLASS_AV_REM_CTRL_TARGET && service_uuid != UUID_SERVCLASS_AV_REMOTE_CONTROL ) + return AVRC_BAD_PARAM; + + /* add service class id list */ + class_list[0] = service_uuid; + result &= SDP_AddServiceClassIdList(sdp_handle, count, class_list); + + /* add protocol descriptor list */ + result &= SDP_AddProtocolList(sdp_handle, AVRC_NUM_PROTO_ELEMS, (tSDP_PROTOCOL_ELEM *)avrc_proto_list); + + /* add profile descriptor list */ + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_0); + + /* add supported categories */ + p = temp; + UINT16_TO_BE_STREAM(p, categories); + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, + (UINT32)2, (UINT8*)temp); + + /* add provider name */ + if (p_provider_name != NULL) + { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_provider_name)+1), (UINT8 *) p_provider_name); + } + + /* add service name */ + if (p_service_name != NULL) + { + result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name); + } + + /* add browse group list */ + browse_list[0] = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list); + + + return (result ? AVRC_SUCCESS : AVRC_FAIL); +} + + + +/****************************************************************************** +** +** Function AVRC_SetTraceLevel +** +** Description Sets the trace level for AVRC. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVRC tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +UINT8 AVRC_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + avrc_cb.trace_level = new_level; + + return (avrc_cb.trace_level); +} + +/******************************************************************************* +** +** Function AVRC_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +void AVRC_Init(void) +{ + memset(&avrc_cb, 0, sizeof(tAVRC_CB)); + +#if defined(AVRC_INITIAL_TRACE_LEVEL) + avrc_cb.trace_level = AVRC_INITIAL_TRACE_LEVEL; +#else + avrc_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + diff --git a/stack/bnep/bnep_api.c b/stack/bnep/bnep_api.c new file mode 100644 index 0000000..fbbf179 --- /dev/null +++ b/stack/bnep/bnep_api.c @@ -0,0 +1,781 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the BNEP API code + * + ******************************************************************************/ + +#include +#include "bnep_api.h" +#include "bnep_int.h" + +/******************************************************************************* +** +** Function BNEP_Init +** +** Description This function initializes the BNEP unit. It should be called +** before accessing any other APIs to initialize the control block +** +** Returns void +** +*******************************************************************************/ +void BNEP_Init (void) +{ + memset (&bnep_cb, 0, sizeof (tBNEP_CB)); + +#if defined(BNEP_INITIAL_TRACE_LEVEL) + bnep_cb.trace_level = BNEP_INITIAL_TRACE_LEVEL; +#else + bnep_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + /* Start a timer to read our BD address */ + btu_start_timer (&bnep_cb.bnep_tle, BTU_TTYPE_BNEP, 2); +} + + +/******************************************************************************* +** +** Function BNEP_Register +** +** Description This function is called by the upper layer to register +** its callbacks with BNEP +** +** Parameters: p_reg_info - contains all callback function pointers +** +** +** Returns BNEP_SUCCESS if registered successfully +** BNEP_FAILURE if connection state callback is missing +** +*******************************************************************************/ +tBNEP_RESULT BNEP_Register (tBNEP_REGISTER *p_reg_info) +{ + /* There should be connection state call back registered */ + if ((!p_reg_info) || (!(p_reg_info->p_conn_state_cb))) + return BNEP_SECURITY_FAIL; + + bnep_cb.p_conn_ind_cb = p_reg_info->p_conn_ind_cb; + bnep_cb.p_conn_state_cb = p_reg_info->p_conn_state_cb; + bnep_cb.p_data_ind_cb = p_reg_info->p_data_ind_cb; + bnep_cb.p_data_buf_cb = p_reg_info->p_data_buf_cb; + bnep_cb.p_filter_ind_cb = p_reg_info->p_filter_ind_cb; + bnep_cb.p_mfilter_ind_cb = p_reg_info->p_mfilter_ind_cb; + bnep_cb.p_tx_data_flow_cb = p_reg_info->p_tx_data_flow_cb; + + if (bnep_register_with_l2cap ()) + return BNEP_SECURITY_FAIL; + + bnep_cb.profile_registered = TRUE; + BTM_GetLocalDeviceAddr (bnep_cb.my_bda); + return BNEP_SUCCESS; +} + + +/******************************************************************************* +** +** Function BNEP_Deregister +** +** Description This function is called by the upper layer to de-register +** its callbacks. +** +** Parameters: void +** +** +** Returns void +** +*******************************************************************************/ +void BNEP_Deregister (void) +{ + /* Clear all the call backs registered */ + bnep_cb.p_conn_ind_cb = NULL; + bnep_cb.p_conn_state_cb = NULL; + bnep_cb.p_data_ind_cb = NULL; + bnep_cb.p_data_buf_cb = NULL; + bnep_cb.p_filter_ind_cb = NULL; + bnep_cb.p_mfilter_ind_cb = NULL; + + bnep_cb.profile_registered = FALSE; + L2CA_Deregister (BT_PSM_BNEP); +} + + +/******************************************************************************* +** +** Function BNEP_Connect +** +** Description This function creates a BNEP connection to a remote +** device. +** +** Parameters: p_rem_addr - BD_ADDR of the peer +** src_uuid - source uuid for the connection +** dst_uuid - destination uuid for the connection +** p_handle - pointer to return the handle for the connection +** +** Returns BNEP_SUCCESS if connection started +** BNEP_NO_RESOURCES if no resources +** +*******************************************************************************/ +tBNEP_RESULT BNEP_Connect (BD_ADDR p_rem_bda, + tBT_UUID *src_uuid, + tBT_UUID *dst_uuid, + UINT16 *p_handle) +{ + UINT16 cid; + tBNEP_CONN *p_bcb = bnepu_find_bcb_by_bd_addr (p_rem_bda); + + BNEP_TRACE_API6 ("BNEP_Connect() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + p_rem_bda[0], p_rem_bda[1], p_rem_bda[2], + p_rem_bda[3], p_rem_bda[4], p_rem_bda[5]); + + if (!bnep_cb.profile_registered) + return BNEP_WRONG_STATE; + + /* Both source and destination UUID lengths should be same */ + if (src_uuid->len != dst_uuid->len) + return BNEP_CONN_FAILED_UUID_SIZE; + +#if (!defined (BNEP_SUPPORTS_ALL_UUID_LENGTHS) || BNEP_SUPPORTS_ALL_UUID_LENGTHS == FALSE) + if (src_uuid->len != 2) + return BNEP_CONN_FAILED_UUID_SIZE; +#endif + + if (!p_bcb) + { + if ((p_bcb = bnepu_allocate_bcb (p_rem_bda)) == NULL) + return (BNEP_NO_RESOURCES); + } + else if (p_bcb->con_state != BNEP_STATE_CONNECTED) + return BNEP_WRONG_STATE; + else + { + /* Backup current UUID values to restore if role change fails */ + memcpy ((UINT8 *)&(p_bcb->prv_src_uuid), (UINT8 *)&(p_bcb->src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->prv_dst_uuid), (UINT8 *)&(p_bcb->dst_uuid), sizeof (tBT_UUID)); + } + + /* We are the originator of this connection */ + p_bcb->con_flags |= BNEP_FLAGS_IS_ORIG; + + memcpy ((UINT8 *)&(p_bcb->src_uuid), (UINT8 *)src_uuid, sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->dst_uuid), (UINT8 *)dst_uuid, sizeof (tBT_UUID)); + + if (p_bcb->con_state == BNEP_STATE_CONNECTED) + { + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_bcb->con_state = BNEP_STATE_SEC_CHECKING; + + BNEP_TRACE_API1 ("BNEP initiating security procedures for src uuid 0x%x", + p_bcb->src_uuid.uu.uuid16); + +#if (defined (BNEP_DO_AUTH_FOR_ROLE_SWITCH) && BNEP_DO_AUTH_FOR_ROLE_SWITCH == TRUE) + btm_sec_mx_access_request (p_bcb->rem_bda, BT_PSM_BNEP, TRUE, + BTM_SEC_PROTO_BNEP, + bnep_get_uuid32(src_uuid), + &bnep_sec_check_complete, p_bcb); +#else + bnep_sec_check_complete (p_bcb->rem_bda, p_bcb, BTM_SUCCESS); +#endif + + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_bcb->con_state = BNEP_STATE_CONN_START; + + if ((cid = L2CA_ConnectReq (BT_PSM_BNEP, p_bcb->rem_bda)) != 0) + { + p_bcb->l2cap_cid = cid; + + } + else + { + BNEP_TRACE_ERROR0 ("BNEP - Originate failed"); + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_FAILED, FALSE); + bnepu_release_bcb (p_bcb); + return BNEP_CONN_FAILED; + } + + /* Start timer waiting for connect */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_CONN_TIMEOUT); + } + + *p_handle = p_bcb->handle; + return (BNEP_SUCCESS); +} + + +/******************************************************************************* +** +** Function BNEP_ConnectResp +** +** Description This function is called in responce to connection indication +** +** +** Parameters: handle - handle given in the connection indication +** resp - responce for the connection indication +** +** Returns BNEP_SUCCESS if connection started +** BNEP_WRONG_HANDLE if the connection is not found +** BNEP_WRONG_STATE if the responce is not expected +** +*******************************************************************************/ +tBNEP_RESULT BNEP_ConnectResp (UINT16 handle, tBNEP_RESULT resp) +{ + tBNEP_CONN *p_bcb; + UINT16 resp_code = BNEP_SETUP_CONN_OK; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + if (p_bcb->con_state != BNEP_STATE_CONN_SETUP || + (!(p_bcb->con_flags & BNEP_FLAGS_SETUP_RCVD))) + return (BNEP_WRONG_STATE); + + BNEP_TRACE_API2 ("BNEP_ConnectResp() for handle %d, responce %d", handle, resp); + + /* Form appropriate responce based on profile responce */ + if (resp == BNEP_CONN_FAILED_SRC_UUID) resp_code = BNEP_SETUP_INVALID_SRC_UUID; + else if (resp == BNEP_CONN_FAILED_DST_UUID) resp_code = BNEP_SETUP_INVALID_DEST_UUID; + else if (resp == BNEP_CONN_FAILED_UUID_SIZE) resp_code = BNEP_SETUP_INVALID_UUID_SIZE; + else if (resp == BNEP_SUCCESS) resp_code = BNEP_SETUP_CONN_OK; + else resp_code = BNEP_SETUP_CONN_NOT_ALLOWED; + + bnep_send_conn_responce (p_bcb, resp_code); + p_bcb->con_flags &= (~BNEP_FLAGS_SETUP_RCVD); + + if (resp == BNEP_SUCCESS) + bnep_connected (p_bcb); + else if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + { + /* Restore the original parameters */ + p_bcb->con_state = BNEP_STATE_CONNECTED; + p_bcb->con_flags &= (~BNEP_FLAGS_SETUP_RCVD); + + memcpy ((UINT8 *)&(p_bcb->src_uuid), (UINT8 *)&(p_bcb->prv_src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->dst_uuid), (UINT8 *)&(p_bcb->prv_dst_uuid), sizeof (tBT_UUID)); + } + + /* Process remaining part of the setup message (extension headers) */ + if (p_bcb->p_pending_data) + { + UINT8 extension_present = TRUE, *p, ext_type; + UINT16 rem_len; + + rem_len = p_bcb->p_pending_data->len; + p = (UINT8 *)(p_bcb->p_pending_data + 1) + p_bcb->p_pending_data->offset; + while (extension_present && p && rem_len) + { + ext_type = *p++; + extension_present = ext_type >> 7; + ext_type &= 0x7F; + + /* if unknown extension present stop processing */ + if (ext_type) + break; + + p = bnep_process_control_packet (p_bcb, p, &rem_len, TRUE); + } + + GKI_freebuf (p_bcb->p_pending_data); + p_bcb->p_pending_data = NULL; + } + return (BNEP_SUCCESS); +} + + +/******************************************************************************* +** +** Function BNEP_Disconnect +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - handle of the connection +** +** Returns BNEP_SUCCESS if connection is disconnected +** BNEP_WRONG_HANDLE if no connection is not found +** +*******************************************************************************/ +tBNEP_RESULT BNEP_Disconnect (UINT16 handle) +{ + tBNEP_CONN *p_bcb; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + if (p_bcb->con_state == BNEP_STATE_IDLE) + return (BNEP_WRONG_HANDLE); + + BNEP_TRACE_API1 ("BNEP_Disconnect() for handle %d", handle); + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + bnepu_release_bcb (p_bcb); + + return (BNEP_SUCCESS); +} + + +/******************************************************************************* +** +** Function BNEP_WriteBuf +** +** Description This function sends data in a GKI buffer on BNEP connection +** +** Parameters: handle - handle of the connection to write +** p_dest_addr - BD_ADDR/Ethernet addr of the destination +** p_buf - pointer to address of buffer with data +** protocol - protocol type of the packet +** p_src_addr - (optional) BD_ADDR/ethernet address of the source +** (should be NULL if it is local BD Addr) +** fw_ext_present - forwarded extensions present +** +** Returns: BNEP_WRONG_HANDLE - if passed handle is not valid +** BNEP_MTU_EXCEDED - If the data length is greater than MTU +** BNEP_IGNORE_CMD - If the packet is filtered out +** BNEP_Q_SIZE_EXCEEDED - If the Tx Q is full +** BNEP_SUCCESS - If written successfully +** +*******************************************************************************/ +tBNEP_RESULT BNEP_WriteBuf (UINT16 handle, + UINT8 *p_dest_addr, + BT_HDR *p_buf, + UINT16 protocol, + UINT8 *p_src_addr, + BOOLEAN fw_ext_present) +{ + tBNEP_CONN *p_bcb; + UINT8 *p_data; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + { + GKI_freebuf (p_buf); + return (BNEP_WRONG_HANDLE); + } + + p_bcb = &(bnep_cb.bcb[handle - 1]); + /* Check MTU size */ + if (p_buf->len > BNEP_MTU_SIZE) + { + BNEP_TRACE_ERROR2 ("BNEP_Write() length %d exceeded MTU %d", p_buf->len, BNEP_MTU_SIZE); + GKI_freebuf (p_buf); + return (BNEP_MTU_EXCEDED); + } + + /* Check if the packet should be filtered out */ + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + if (bnep_is_packet_allowed (p_bcb, p_dest_addr, protocol, fw_ext_present, p_data) != BNEP_SUCCESS) + { + /* + ** If packet is filtered and ext headers are present + ** drop the data and forward the ext headers + */ + if (fw_ext_present) + { + UINT8 ext, length; + UINT16 org_len, new_len; + /* parse the extension headers and findout the new packet len */ + org_len = p_buf->len; + new_len = 0; + do { + + ext = *p_data++; + length = *p_data++; + p_data += length; + + new_len += (length + 2); + + if (new_len > org_len) + { + GKI_freebuf (p_buf); + return BNEP_IGNORE_CMD; + } + + } while (ext & 0x80); + + if (protocol != BNEP_802_1_P_PROTOCOL) + protocol = 0; + else + { + new_len += 4; + p_data[2] = 0; + p_data[3] = 0; + } + p_buf->len = new_len; + } + else + { + GKI_freebuf (p_buf); + return BNEP_IGNORE_CMD; + } + } + + /* Check transmit queue */ + if (p_bcb->xmit_q.count >= BNEP_MAX_XMITQ_DEPTH) + { + GKI_freebuf (p_buf); + return (BNEP_Q_SIZE_EXCEEDED); + } + + /* Build the BNEP header */ + bnepu_build_bnep_hdr (p_bcb, p_buf, protocol, p_src_addr, p_dest_addr, fw_ext_present); + + /* Send the data or queue it up */ + bnepu_check_send_packet (p_bcb, p_buf); + + return (BNEP_SUCCESS); +} + + +/******************************************************************************* +** +** Function BNEP_Write +** +** Description This function sends data over a BNEP connection +** +** Parameters: handle - handle of the connection to write +** p_dest_addr - BD_ADDR/Ethernet addr of the destination +** p_data - pointer to data start +** protocol - protocol type of the packet +** p_src_addr - (optional) BD_ADDR/ethernet address of the source +** (should be NULL if it is local BD Addr) +** fw_ext_present - forwarded extensions present +** +** Returns: BNEP_WRONG_HANDLE - if passed handle is not valid +** BNEP_MTU_EXCEDED - If the data length is greater than MTU +** BNEP_IGNORE_CMD - If the packet is filtered out +** BNEP_Q_SIZE_EXCEEDED - If the Tx Q is full +** BNEP_NO_RESOURCES - If not able to allocate a buffer +** BNEP_SUCCESS - If written successfully +** +*******************************************************************************/ +tBNEP_RESULT BNEP_Write (UINT16 handle, + UINT8 *p_dest_addr, + UINT8 *p_data, + UINT16 len, + UINT16 protocol, + UINT8 *p_src_addr, + BOOLEAN fw_ext_present) +{ + BT_HDR *p_buf; + tBNEP_CONN *p_bcb; + UINT8 *p; + + /* Check MTU size. Consider the possibility of having extension headers */ + if (len > BNEP_MTU_SIZE) + { + BNEP_TRACE_ERROR2 ("BNEP_Write() length %d exceeded MTU %d", len, BNEP_MTU_SIZE); + return (BNEP_MTU_EXCEDED); + } + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + /* Check if the packet should be filtered out */ + if (bnep_is_packet_allowed (p_bcb, p_dest_addr, protocol, fw_ext_present, p_data) != BNEP_SUCCESS) + { + /* + ** If packet is filtered and ext headers are present + ** drop the data and forward the ext headers + */ + if (fw_ext_present) + { + UINT8 ext, length; + UINT16 org_len, new_len; + /* parse the extension headers and findout the new packet len */ + org_len = len; + new_len = 0; + p = p_data; + do { + + ext = *p_data++; + length = *p_data++; + p_data += length; + + new_len += (length + 2); + + if (new_len > org_len) + return BNEP_IGNORE_CMD; + + } while (ext & 0x80); + + if (protocol != BNEP_802_1_P_PROTOCOL) + protocol = 0; + else + { + new_len += 4; + p_data[2] = 0; + p_data[3] = 0; + } + len = new_len; + p_data = p; + } + else + return BNEP_IGNORE_CMD; + } + + /* Check transmit queue */ + if (p_bcb->xmit_q.count >= BNEP_MAX_XMITQ_DEPTH) + return (BNEP_Q_SIZE_EXCEEDED); + + /* Get a buffer to copy teh data into */ + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP_Write() not able to get buffer"); + return (BNEP_NO_RESOURCES); + } + + p_buf->len = len; + p_buf->offset = BNEP_MINIMUM_OFFSET; + p = (UINT8 *)(p_buf + 1) + BNEP_MINIMUM_OFFSET; + + memcpy (p, p_data, len); + + /* Build the BNEP header */ + bnepu_build_bnep_hdr (p_bcb, p_buf, protocol, p_src_addr, p_dest_addr, fw_ext_present); + + /* Send the data or queue it up */ + bnepu_check_send_packet (p_bcb, p_buf); + + return (BNEP_SUCCESS); +} + + +/******************************************************************************* +** +** Function BNEP_SetProtocolFilters +** +** Description This function sets the protocol filters on peer device +** +** Parameters: handle - Handle for the connection +** num_filters - total number of filter ranges +** p_start_array - Array of beginings of all protocol ranges +** p_end_array - Array of ends of all protocol ranges +** +** Returns BNEP_WRONG_HANDLE - if the connection handle is not valid +** BNEP_SET_FILTER_FAIL - if the connection is in wrong state +** BNEP_TOO_MANY_FILTERS - if too many filters +** BNEP_SUCCESS - if request sent successfully +** +*******************************************************************************/ +tBNEP_RESULT BNEP_SetProtocolFilters (UINT16 handle, + UINT16 num_filters, + UINT16 *p_start_array, + UINT16 *p_end_array) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + UINT16 xx; + tBNEP_CONN *p_bcb; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + /* Check the connection state */ + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + return (BNEP_WRONG_STATE); + + /* Validate the parameters */ + if (num_filters && (!p_start_array || !p_end_array)) + return (BNEP_SET_FILTER_FAIL); + + if (num_filters > BNEP_MAX_PROT_FILTERS) + return (BNEP_TOO_MANY_FILTERS); + + /* Fill the filter values in connnection block */ + for (xx = 0; xx < num_filters; xx++) + { + p_bcb->sent_prot_filter_start[xx] = *p_start_array++; + p_bcb->sent_prot_filter_end[xx] = *p_end_array++; + } + + p_bcb->sent_num_filters = num_filters; + + bnepu_send_peer_our_filters (p_bcb); + + return (BNEP_SUCCESS); +#else + return (BNEP_SET_FILTER_FAIL); +#endif +} + + +/******************************************************************************* +** +** Function BNEP_SetMulticastFilters +** +** Description This function sets the filters for multicast addresses for BNEP. +** +** Parameters: handle - Handle for the connection +** num_filters - total number of filter ranges +** p_start_array - Pointer to sequence of beginings of all +** multicast address ranges +** p_end_array - Pointer to sequence of ends of all +** multicast address ranges +** +** Returns BNEP_WRONG_HANDLE - if the connection handle is not valid +** BNEP_SET_FILTER_FAIL - if the connection is in wrong state +** BNEP_TOO_MANY_FILTERS - if too many filters +** BNEP_SUCCESS - if request sent successfully +** +*******************************************************************************/ +tBNEP_RESULT BNEP_SetMulticastFilters (UINT16 handle, + UINT16 num_filters, + UINT8 *p_start_array, + UINT8 *p_end_array) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + UINT16 xx; + tBNEP_CONN *p_bcb; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + /* Check the connection state */ + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + return (BNEP_WRONG_STATE); + + /* Validate the parameters */ + if (num_filters && (!p_start_array || !p_end_array)) + return (BNEP_SET_FILTER_FAIL); + + if (num_filters > BNEP_MAX_MULTI_FILTERS) + return (BNEP_TOO_MANY_FILTERS); + + /* Fill the multicast filter values in connnection block */ + for (xx = 0; xx < num_filters; xx++) + { + memcpy (p_bcb->sent_mcast_filter_start[xx], p_start_array, BD_ADDR_LEN); + memcpy (p_bcb->sent_mcast_filter_end[xx], p_end_array, BD_ADDR_LEN); + + p_start_array += BD_ADDR_LEN; + p_end_array += BD_ADDR_LEN; + } + + p_bcb->sent_mcast_filters = num_filters; + + bnepu_send_peer_our_multi_filters (p_bcb); + + return (BNEP_SUCCESS); +#else + return (BNEP_SET_FILTER_FAIL); +#endif +} + + +/******************************************************************************* +** +** Function BNEP_GetMyBdAddr +** +** Description This function returns a pointer to the local device BD address. +** If the BD address has not been read yet, it returns NULL. +** +** Returns the BD address +** +*******************************************************************************/ +UINT8 *BNEP_GetMyBdAddr (void) +{ + if (bnep_cb.got_my_bd_addr) + return (bnep_cb.my_bda); + else + return (NULL); +} + +/******************************************************************************* +** +** Function BNEP_SetTraceLevel +** +** Description This function sets the trace level for BNEP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 BNEP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + bnep_cb.trace_level = new_level; + + return (bnep_cb.trace_level); +} + + +/******************************************************************************* +** +** Function BNEP_GetStatus +** +** Description This function gets the status information for BNEP connection +** +** Returns BNEP_SUCCESS - if the status is available +** BNEP_NO_RESOURCES - if no structure is passed for output +** BNEP_WRONG_HANDLE - if the handle is invalid +** BNEP_WRONG_STATE - if not in connected state +** +*******************************************************************************/ +tBNEP_RESULT BNEP_GetStatus (UINT16 handle, tBNEP_STATUS *p_status) +{ +#if (defined (BNEP_SUPPORTS_STATUS_API) && BNEP_SUPPORTS_STATUS_API == TRUE) + tBNEP_CONN *p_bcb; + + if (!p_status) + return BNEP_NO_RESOURCES; + + if ((!handle) || (handle > BNEP_MAX_CONNECTIONS)) + return (BNEP_WRONG_HANDLE); + + p_bcb = &(bnep_cb.bcb[handle - 1]); + + memset (p_status, 0, sizeof (tBNEP_STATUS)); + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + return BNEP_WRONG_STATE; + + /* Read the status parameters from the connection control block */ + p_status->con_status = BNEP_STATUS_CONNECTED; + p_status->l2cap_cid = p_bcb->l2cap_cid; + p_status->rem_mtu_size = p_bcb->rem_mtu_size; + p_status->xmit_q_depth = p_bcb->xmit_q.count; + p_status->sent_num_filters = p_bcb->sent_num_filters; + p_status->sent_mcast_filters = p_bcb->sent_mcast_filters; + p_status->rcvd_num_filters = p_bcb->rcvd_num_filters; + p_status->rcvd_mcast_filters = p_bcb->rcvd_mcast_filters; + + memcpy (p_status->rem_bda, p_bcb->rem_bda, BD_ADDR_LEN); + memcpy (&(p_status->src_uuid), &(p_bcb->src_uuid), sizeof (tBT_UUID)); + memcpy (&(p_status->dst_uuid), &(p_bcb->dst_uuid), sizeof (tBT_UUID)); + + return BNEP_SUCCESS; +#else + return (BNEP_IGNORE_CMD); +#endif +} + + diff --git a/stack/bnep/bnep_int.h b/stack/bnep/bnep_int.h new file mode 100644 index 0000000..528c809 --- /dev/null +++ b/stack/bnep/bnep_int.h @@ -0,0 +1,251 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used BNEP definitions + * + ******************************************************************************/ + +#ifndef BNEP_INT_H +#define BNEP_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "bnep_api.h" +#include "btm_int.h" +#include "btu.h" + + +/* BNEP frame types +*/ +#define BNEP_FRAME_GENERAL_ETHERNET 0x00 +#define BNEP_FRAME_CONTROL 0x01 +#define BNEP_FRAME_COMPRESSED_ETHERNET 0x02 +#define BNEP_FRAME_COMPRESSED_ETHERNET_SRC_ONLY 0x03 +#define BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY 0x04 + + +/* BNEP filter control message types +*/ +#define BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD 0x00 +#define BNEP_SETUP_CONNECTION_REQUEST_MSG 0x01 +#define BNEP_SETUP_CONNECTION_RESPONSE_MSG 0x02 +#define BNEP_FILTER_NET_TYPE_SET_MSG 0x03 +#define BNEP_FILTER_NET_TYPE_RESPONSE_MSG 0x04 +#define BNEP_FILTER_MULTI_ADDR_SET_MSG 0x05 +#define BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG 0x06 + + +/* BNEP header extension types +*/ +#define BNEP_EXTENSION_FILTER_CONTROL 0x00 + + +/* BNEP Setup Connection response codes +*/ +#define BNEP_SETUP_CONN_OK 0x0000 +#define BNEP_SETUP_INVALID_DEST_UUID 0x0001 +#define BNEP_SETUP_INVALID_SRC_UUID 0x0002 +#define BNEP_SETUP_INVALID_UUID_SIZE 0x0003 +#define BNEP_SETUP_CONN_NOT_ALLOWED 0x0004 + + +/* BNEP filter control response codes +*/ +#define BNEP_FILTER_CRL_OK 0x0000 +#define BNEP_FILTER_CRL_UNSUPPORTED 0x0001 +#define BNEP_FILTER_CRL_BAD_RANGE 0x0002 +#define BNEP_FILTER_CRL_MAX_REACHED 0x0003 +#define BNEP_FILTER_CRL_SECURITY_ERR 0x0004 + + +/* 802.1p protocol packet will have actual protocol field in side the payload */ +#define BNEP_802_1_P_PROTOCOL 0x8100 + +/* Timeout definitions. +*/ +#define BNEP_CONN_TIMEOUT 20 /* Connection related timeout */ +#define BNEP_HOST_TIMEOUT 200 /* host responce timeout */ +#define BNEP_FILTER_SET_TIMEOUT 10 + +/* Define the Out-Flow default values. */ +#define BNEP_OFLOW_QOS_FLAG 0 +#define BNEP_OFLOW_SERV_TYPE 0 +#define BNEP_OFLOW_TOKEN_RATE 0 +#define BNEP_OFLOW_TOKEN_BUCKET_SIZE 0 +#define BNEP_OFLOW_PEAK_BANDWIDTH 0 +#define BNEP_OFLOW_LATENCY 0 +#define BNEP_OFLOW_DELAY_VARIATION 0 + +/* Define the In-Flow default values. */ +#define BNEP_IFLOW_QOS_FLAG 0 +#define BNEP_IFLOW_SERV_TYPE 0 +#define BNEP_IFLOW_TOKEN_RATE 0 +#define BNEP_IFLOW_TOKEN_BUCKET_SIZE 0 +#define BNEP_IFLOW_PEAK_BANDWIDTH 0 +#define BNEP_IFLOW_LATENCY 0 +#define BNEP_IFLOW_DELAY_VARIATION 0 + +#define BNEP_FLUSH_TO 0xFFFF + +#define BNEP_MAX_RETRANSMITS 3 + +/* Define the BNEP Connection Control Block +*/ +typedef struct +{ +#define BNEP_STATE_IDLE 0 +#define BNEP_STATE_CONN_START 1 +#define BNEP_STATE_CFG_SETUP 2 +#define BNEP_STATE_CONN_SETUP 3 +#define BNEP_STATE_SEC_CHECKING 4 +#define BNEP_STATE_SETUP_RCVD 5 +#define BNEP_STATE_CONNECTED 6 + UINT8 con_state; + +#define BNEP_FLAGS_IS_ORIG 0x01 +#define BNEP_FLAGS_HIS_CFG_DONE 0x02 +#define BNEP_FLAGS_MY_CFG_DONE 0x04 +#define BNEP_FLAGS_L2CAP_CONGESTED 0x08 +#define BNEP_FLAGS_FILTER_RESP_PEND 0x10 +#define BNEP_FLAGS_MULTI_RESP_PEND 0x20 +#define BNEP_FLAGS_SETUP_RCVD 0x40 +#define BNEP_FLAGS_CONN_COMPLETED 0x80 + UINT8 con_flags; + BT_HDR *p_pending_data; + + UINT16 l2cap_cid; + BD_ADDR rem_bda; + UINT16 rem_mtu_size; + TIMER_LIST_ENT conn_tle; + BUFFER_Q xmit_q; + + UINT16 sent_num_filters; + UINT16 sent_prot_filter_start[BNEP_MAX_PROT_FILTERS]; + UINT16 sent_prot_filter_end[BNEP_MAX_PROT_FILTERS]; + + UINT16 sent_mcast_filters; + BD_ADDR sent_mcast_filter_start[BNEP_MAX_MULTI_FILTERS]; + BD_ADDR sent_mcast_filter_end[BNEP_MAX_MULTI_FILTERS]; + + UINT16 rcvd_num_filters; + UINT16 rcvd_prot_filter_start[BNEP_MAX_PROT_FILTERS]; + UINT16 rcvd_prot_filter_end[BNEP_MAX_PROT_FILTERS]; + + UINT16 rcvd_mcast_filters; + BD_ADDR rcvd_mcast_filter_start[BNEP_MAX_MULTI_FILTERS]; + BD_ADDR rcvd_mcast_filter_end[BNEP_MAX_MULTI_FILTERS]; + + UINT16 bad_pkts_rcvd; + UINT8 re_transmits; + UINT16 handle; + tBT_UUID prv_src_uuid; + tBT_UUID prv_dst_uuid; + tBT_UUID src_uuid; + tBT_UUID dst_uuid; + +} tBNEP_CONN; + + +/* The main BNEP control block +*/ +typedef struct +{ + tL2CAP_CFG_INFO l2cap_my_cfg; /* My L2CAP config */ + tBNEP_CONN bcb[BNEP_MAX_CONNECTIONS]; + + tBNEP_CONNECT_IND_CB *p_conn_ind_cb; + tBNEP_CONN_STATE_CB *p_conn_state_cb; + tBNEP_DATA_IND_CB *p_data_ind_cb; + tBNEP_DATA_BUF_CB *p_data_buf_cb; + tBNEP_FILTER_IND_CB *p_filter_ind_cb; + tBNEP_MFILTER_IND_CB *p_mfilter_ind_cb; + tBNEP_TX_DATA_FLOW_CB *p_tx_data_flow_cb; + + tL2CAP_APPL_INFO reg_info; + + TIMER_LIST_ENT bnep_tle; + BOOLEAN profile_registered; /* TRUE when we got our BD addr */ + UINT8 trace_level; + BOOLEAN got_my_bd_addr; /* TRUE when we got our BD addr */ + BD_ADDR my_bda; /* BD Address of this device */ + +} tBNEP_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global BNEP data +*/ +#if BNEP_DYNAMIC_MEMORY == FALSE +BNEP_API extern tBNEP_CB bnep_cb; +#else +BNEP_API extern tBNEP_CB *bnep_cb_ptr; +#define bnep_cb (*bnep_cb_ptr) +#endif + +/* Functions provided by bnep_main.c +*/ +extern tBNEP_RESULT bnep_register_with_l2cap (void); +extern void bnep_disconnect (tBNEP_CONN *p_bcb, UINT16 reason); +extern tBNEP_CONN *bnep_conn_originate (UINT8 *p_bd_addr); +extern void bnep_process_timeout (TIMER_LIST_ENT *p_tle); +extern void bnep_connected (tBNEP_CONN *p_bcb); + + +/* Functions provided by bnep_utils.c +*/ +extern tBNEP_CONN *bnepu_find_bcb_by_cid (UINT16 cid); +extern tBNEP_CONN *bnepu_find_bcb_by_bd_addr (UINT8 *p_bda); +extern tBNEP_CONN *bnepu_allocate_bcb (BD_ADDR p_rem_bda); +extern void bnepu_release_bcb (tBNEP_CONN *p_bcb); +extern void bnepu_send_peer_our_filters (tBNEP_CONN *p_bcb); +extern void bnepu_send_peer_our_multi_filters (tBNEP_CONN *p_bcb); +extern BOOLEAN bnepu_does_dest_support_prot (tBNEP_CONN *p_bcb, UINT16 protocol); +extern void bnepu_build_bnep_hdr (tBNEP_CONN *p_bcb, BT_HDR *p_buf, UINT16 protocol, + UINT8 *p_src_addr, UINT8 *p_dest_addr, BOOLEAN ext_bit); +extern void test_bnepu_build_bnep_hdr (tBNEP_CONN *p_bcb, BT_HDR *p_buf, UINT16 protocol, + UINT8 *p_src_addr, UINT8 *p_dest_addr, UINT8 type); + +extern tBNEP_CONN *bnepu_get_route_to_dest (UINT8 *p_bda); +extern void bnepu_check_send_packet (tBNEP_CONN *p_bcb, BT_HDR *p_buf); +extern void bnep_send_command_not_understood (tBNEP_CONN *p_bcb, UINT8 cmd_code); +extern void bnepu_process_peer_filter_set (tBNEP_CONN *p_bcb, UINT8 *p_filters, UINT16 len); +extern void bnepu_process_peer_filter_rsp (tBNEP_CONN *p_bcb, UINT8 *p_data); +extern void bnepu_process_multicast_filter_rsp (tBNEP_CONN *p_bcb, UINT8 *p_data); +extern void bnep_send_conn_req (tBNEP_CONN *p_bcb); +extern void bnep_send_conn_responce (tBNEP_CONN *p_bcb, UINT16 resp_code); +extern void bnep_process_setup_conn_req (tBNEP_CONN *p_bcb, UINT8 *p_setup, UINT8 len); +extern void bnep_process_setup_conn_responce (tBNEP_CONN *p_bcb, UINT8 *p_setup); +extern UINT8 *bnep_process_control_packet (tBNEP_CONN *p_bcb, UINT8 *p, UINT16 *len, BOOLEAN is_ext); +extern void bnep_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 result); +extern tBNEP_RESULT bnep_is_packet_allowed (tBNEP_CONN *p_bcb, BD_ADDR p_dest_addr, UINT16 protocol, BOOLEAN fw_ext_present, UINT8 *p_data); +extern UINT32 bnep_get_uuid32 (tBT_UUID *src_uuid); +extern void bnep_dump_status (void); + + + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/stack/bnep/bnep_main.c b/stack/bnep/bnep_main.c new file mode 100644 index 0000000..12c9da0 --- /dev/null +++ b/stack/bnep/bnep_main.c @@ -0,0 +1,838 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main BNEP functions + * + ******************************************************************************/ + +#include "bt_target.h" +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "l2c_api.h" +#include "l2cdefs.h" + +#include "btu.h" +#include "btm_api.h" + +#include "bnep_api.h" +#include "bnep_int.h" + + +/********************************************************************************/ +/* G L O B A L B N E P D A T A */ +/********************************************************************************/ +#if BNEP_DYNAMIC_MEMORY == FALSE +tBNEP_CB bnep_cb; +#endif + +const UINT16 bnep_frame_hdr_sizes[] = {14, 1, 2, 8, 8}; + +/********************************************************************************/ +/* 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 bnep_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id); +static void bnep_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void bnep_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void bnep_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void bnep_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void bnep_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +static void bnep_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void bnep_congestion_ind (UINT16 lcid, BOOLEAN is_congested); + +static void bnep_read_addr_cb (void *p_bda); + + +/******************************************************************************* +** +** Function bnep_register_with_l2cap +** +** Description This function registers BNEP PSM with L2CAP +** +** Returns void +** +*******************************************************************************/ +tBNEP_RESULT bnep_register_with_l2cap (void) +{ + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + memset(&bnep_cb.l2cap_my_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + bnep_cb.l2cap_my_cfg.mtu_present = TRUE; + bnep_cb.l2cap_my_cfg.mtu = BNEP_MTU_SIZE; + bnep_cb.l2cap_my_cfg.flush_to_present = TRUE; + bnep_cb.l2cap_my_cfg.flush_to = BNEP_FLUSH_TO; + + bnep_cb.reg_info.pL2CA_ConnectInd_Cb = bnep_connect_ind; + bnep_cb.reg_info.pL2CA_ConnectCfm_Cb = bnep_connect_cfm; + bnep_cb.reg_info.pL2CA_ConfigInd_Cb = bnep_config_ind; + bnep_cb.reg_info.pL2CA_ConfigCfm_Cb = bnep_config_cfm; + bnep_cb.reg_info.pL2CA_DisconnectInd_Cb = bnep_disconnect_ind; + bnep_cb.reg_info.pL2CA_DisconnectCfm_Cb = bnep_disconnect_cfm; + bnep_cb.reg_info.pL2CA_DataInd_Cb = bnep_data_ind; + bnep_cb.reg_info.pL2CA_CongestionStatus_Cb = bnep_congestion_ind; + + /* Now, register with L2CAP */ + if (!L2CA_Register (BT_PSM_BNEP, &bnep_cb.reg_info)) + { + BNEP_TRACE_ERROR0 ("BNEP - Registration failed"); + return BNEP_SECURITY_FAIL; + } + + return BNEP_SUCCESS; +} + + +/******************************************************************************* +** +** Function bnep_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 bnep_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + tBNEP_CONN *p_bcb = bnepu_find_bcb_by_bd_addr (bd_addr); + + /* If we are not acting as server, or already have a connection, or have */ + /* no more resources to handle the connection, reject the connection. */ + if (!(bnep_cb.profile_registered) || (p_bcb) + || ((p_bcb = bnepu_allocate_bcb(bd_addr)) == NULL)) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0); + return; + } + + /* Transition to the next appropriate state, waiting for config setup. */ + p_bcb->con_state = BNEP_STATE_CFG_SETUP; + + /* Save the L2CAP Channel ID. */ + p_bcb->l2cap_cid = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &bnep_cb.l2cap_my_cfg); + + /* Start timer waiting for config setup */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_CONN_TIMEOUT); + + BNEP_TRACE_EVENT1("BNEP - Rcvd L2CAP conn ind, CID: 0x%x", p_bcb->l2cap_cid); + +} + + +/******************************************************************************* +** +** Function bnep_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 bnep_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tBNEP_CONN *bcb; + + /* Find CCB based on CID */ + if ((bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid); + return; + } + + /* If the connection response contains success status, then */ + /* Transition to the next state and startup the timer. */ + if ((result == L2CAP_CONN_OK) && (bcb->con_state == BNEP_STATE_CONN_START)) + { + bcb->con_state = BNEP_STATE_CFG_SETUP; + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &bnep_cb.l2cap_my_cfg); + + /* Start timer waiting for config results */ + btu_start_timer (&bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_CONN_TIMEOUT); + + BNEP_TRACE_EVENT1 ("BNEP - got conn cnf, sent cfg req, CID: 0x%x", bcb->l2cap_cid); + } + else + { + BNEP_TRACE_WARNING2 ("BNEP - Rcvd conn cnf with error: 0x%x CID 0x%x", result, bcb->l2cap_cid); + + /* Tell the upper layer, if he has a callback */ + if (bnep_cb.p_conn_state_cb && + bcb->con_flags & BNEP_FLAGS_IS_ORIG) + { + (*bnep_cb.p_conn_state_cb) (bcb->handle, bcb->rem_bda, BNEP_CONN_FAILED, FALSE); + } + + bnepu_release_bcb (bcb); + } +} + +/******************************************************************************* +** +** Function bnep_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void bnep_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tBNEP_CONN *p_bcb; + UINT16 result, mtu = 0; + + /* Find CCB based on CID */ + if ((p_bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + BNEP_TRACE_EVENT1 ("BNEP - Rcvd cfg ind, CID: 0x%x", l2cap_cid); + + /* Remember the remote MTU size */ + if ((!p_cfg->mtu_present) || (p_cfg->mtu < BNEP_MIN_MTU_SIZE)) + { + mtu = p_cfg->mtu; + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = TRUE; + p_cfg->mtu = BNEP_MIN_MTU_SIZE; + p_cfg->result = result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + } + else + { + if (p_cfg->mtu > BNEP_MTU_SIZE) + p_bcb->rem_mtu_size = BNEP_MTU_SIZE; + else + p_bcb->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 = result = L2CAP_CFG_OK; + } + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + if (result != L2CAP_CFG_OK) + { + BNEP_TRACE_EVENT2 ("BNEP - Rcvd cfg ind with bad MTU %d, CID: 0x%x", mtu, l2cap_cid); + return; + } + + p_bcb->con_flags |= BNEP_FLAGS_HIS_CFG_DONE; + + if (p_bcb->con_flags & BNEP_FLAGS_MY_CFG_DONE) + { + p_bcb->con_state = BNEP_STATE_SEC_CHECKING; + + /* Start timer waiting for setup or response */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_HOST_TIMEOUT); + + if (p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) + { + btm_sec_mx_access_request (p_bcb->rem_bda, BT_PSM_BNEP, TRUE, + BTM_SEC_PROTO_BNEP, + bnep_get_uuid32(&(p_bcb->src_uuid)), + &bnep_sec_check_complete, p_bcb); + } + } +} + + +/******************************************************************************* +** +** Function bnep_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void bnep_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tBNEP_CONN *p_bcb; + + BNEP_TRACE_EVENT2 ("BNEP - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if ((p_bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* For now, always accept configuration from the other side */ + if (p_cfg->result == L2CAP_CFG_OK) + { + p_bcb->con_flags |= BNEP_FLAGS_MY_CFG_DONE; + + if (p_bcb->con_flags & BNEP_FLAGS_HIS_CFG_DONE) + { + p_bcb->con_state = BNEP_STATE_SEC_CHECKING; + + /* Start timer waiting for setup or response */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_HOST_TIMEOUT); + + if (p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) + { + btm_sec_mx_access_request (p_bcb->rem_bda, BT_PSM_BNEP, TRUE, + BTM_SEC_PROTO_BNEP, + bnep_get_uuid32(&(p_bcb->src_uuid)), + &bnep_sec_check_complete, p_bcb); + } + } + } + else + { + /* Tell the upper layer, if he has a callback */ + if ((p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) && (bnep_cb.p_conn_state_cb)) + { + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_FAILED_CFG, FALSE); + } + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + bnepu_release_bcb (p_bcb); + } +} + + +/******************************************************************************* +** +** Function bnep_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 bnep_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tBNEP_CONN *p_bcb; + + if (ack_needed) + L2CA_DisconnectRsp (l2cap_cid); + + /* Find CCB based on CID */ + if ((p_bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + BNEP_TRACE_EVENT1 ("BNEP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + /* Tell the user if he has a callback */ + if (p_bcb->con_state == BNEP_STATE_CONNECTED) + { + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb)(p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_DISCONNECTED, FALSE); + } + else + { + if (((p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) && (bnep_cb.p_conn_state_cb)) || + p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_FAILED, FALSE); + } + + bnepu_release_bcb (p_bcb); +} + + + +/******************************************************************************* +** +** Function bnep_disconnect_cfm +** +** Description This function gets the disconnect confirm event from L2CAP +** +** Returns void +** +*******************************************************************************/ +static void bnep_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + BNEP_TRACE_EVENT2 ("BNEP - Rcvd L2CAP disc cfm, CID: 0x%x, Result 0x%x", l2cap_cid, result); +} + + + +/******************************************************************************* +** +** Function bnep_congestion_ind +** +** Description This is a callback function called by L2CAP when +** congestion status changes +** +*******************************************************************************/ +static void bnep_congestion_ind (UINT16 l2cap_cid, BOOLEAN is_congested) +{ + tBNEP_CONN *p_bcb; + + /* Find BCB based on CID */ + if ((p_bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd L2CAP cong, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (is_congested) + { + p_bcb->con_flags |= BNEP_FLAGS_L2CAP_CONGESTED; + if(bnep_cb.p_tx_data_flow_cb) + { + bnep_cb.p_tx_data_flow_cb(p_bcb->handle, BNEP_TX_FLOW_OFF); + } + } + else + { + p_bcb->con_flags &= ~BNEP_FLAGS_L2CAP_CONGESTED; + + if(bnep_cb.p_tx_data_flow_cb) + { + bnep_cb.p_tx_data_flow_cb(p_bcb->handle, BNEP_TX_FLOW_ON); + } + + /* While not congested, send as many buffers as we can */ + while (!(p_bcb->con_flags & BNEP_FLAGS_L2CAP_CONGESTED)) + { + BT_HDR *p_buf = (BT_HDR *)GKI_dequeue (&p_bcb->xmit_q); + + if (!p_buf) + break; + + L2CA_DataWrite (l2cap_cid, p_buf); + } + } +} + + + +/******************************************************************************* +** +** Function bnep_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void bnep_data_ind (UINT16 l2cap_cid, BT_HDR *p_buf) +{ + tBNEP_CONN *p_bcb; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT16 rem_len = p_buf->len; + UINT8 type, ctrl_type, ext_type = 0; + BOOLEAN extension_present, fw_ext_present; + UINT16 protocol = 0; + UINT8 *p_src_addr, *p_dst_addr; + + + /* Find CCB based on CID */ + if ((p_bcb = bnepu_find_bcb_by_cid (l2cap_cid)) == NULL) + { + BNEP_TRACE_WARNING1 ("BNEP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + GKI_freebuf (p_buf); + return; + } + + /* Get the type and extension bits */ + type = *p++; + extension_present = type >> 7; + type &= 0x7f; + if ((rem_len <= bnep_frame_hdr_sizes[type]) || (rem_len > BNEP_MTU_SIZE)) + { + BNEP_TRACE_EVENT2 ("BNEP - rcvd frame, bad len: %d type: 0x%02x", p_buf->len, type); + GKI_freebuf (p_buf); + return; + } + + rem_len--; + + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED)) && + (type != BNEP_FRAME_CONTROL)) + { + BNEP_TRACE_WARNING2 ("BNEP - Ignored L2CAP data while in state: %d, CID: 0x%x", + p_bcb->con_state, l2cap_cid); + + if (extension_present) + { + /* + ** When there is no connection if a data packet is received + ** with unknown control extension headers then those should be processed + ** according to complain/ignore law + */ + UINT8 ext, length, *p_data; + UINT16 org_len, new_len; + /* parse the extension headers and process unknown control headers */ + org_len = rem_len; + new_len = 0; + p_data = p; + do { + + ext = *p++; + length = *p++; + p += length; + + if ((!(ext & 0x7F)) && (*p > BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG)) + bnep_send_command_not_understood (p_bcb, *p); + + new_len += (length + 2); + + if (new_len > org_len) + break; + + } while (ext & 0x80); + } + + GKI_freebuf (p_buf); + return; + } + + if (type > BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY) + { + BNEP_TRACE_EVENT1 ("BNEP - rcvd frame, unknown type: 0x%02x", type); + GKI_freebuf (p_buf); + return; + } + + BNEP_TRACE_DEBUG3 ("BNEP - rcv frame, type: %d len: %d Ext: %d", type, p_buf->len, extension_present); + + /* Initialize addresses to 'not supplied' */ + p_src_addr = p_dst_addr = NULL; + + switch (type) + { + case BNEP_FRAME_GENERAL_ETHERNET: + p_dst_addr = p; + p += BD_ADDR_LEN; + p_src_addr = p; + p += BD_ADDR_LEN; + BE_STREAM_TO_UINT16 (protocol, p); + rem_len -= 14; + break; + + case BNEP_FRAME_CONTROL: + ctrl_type = *p; + p = bnep_process_control_packet (p_bcb, p, &rem_len, FALSE); + + if (ctrl_type == BNEP_SETUP_CONNECTION_REQUEST_MSG && + p_bcb->con_state != BNEP_STATE_CONNECTED && + extension_present && p && rem_len) + { + p_bcb->p_pending_data = (BT_HDR *)GKI_getbuf (rem_len); + if (p_bcb->p_pending_data) + { + memcpy ((UINT8 *)(p_bcb->p_pending_data + 1), p, rem_len); + p_bcb->p_pending_data->len = rem_len; + p_bcb->p_pending_data->offset = 0; + } + } + else + { + while (extension_present && p && rem_len) + { + ext_type = *p++; + extension_present = ext_type >> 7; + ext_type &= 0x7F; + + /* if unknown extension present stop processing */ + if (ext_type) + break; + + p = bnep_process_control_packet (p_bcb, p, &rem_len, TRUE); + } + } + GKI_freebuf (p_buf); + return; + + case BNEP_FRAME_COMPRESSED_ETHERNET: + BE_STREAM_TO_UINT16 (protocol, p); + rem_len -= 2; + break; + + case BNEP_FRAME_COMPRESSED_ETHERNET_SRC_ONLY: + p_src_addr = p; + p += BD_ADDR_LEN; + BE_STREAM_TO_UINT16 (protocol, p); + rem_len -= 8; + break; + + case BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY: + p_dst_addr = p; + p += BD_ADDR_LEN; + BE_STREAM_TO_UINT16 (protocol, p); + rem_len -= 8; + break; + } + + /* Process the header extension if there is one */ + while (extension_present && p && rem_len) + { + ext_type = *p; + extension_present = ext_type >> 7; + ext_type &= 0x7F; + + /* if unknown extension present stop processing */ + if (ext_type) + { + BNEP_TRACE_EVENT1 ("Data extension type 0x%x found", ext_type); + break; + } + + p++; + rem_len--; + p = bnep_process_control_packet (p_bcb, p, &rem_len, TRUE); + } + + p_buf->offset += p_buf->len - rem_len; + p_buf->len = rem_len; + + /* Always give the upper layer MAC addresses */ + if (!p_src_addr) + p_src_addr = (UINT8 *) p_bcb->rem_bda; + + if (!p_dst_addr) + p_dst_addr = (UINT8 *) bnep_cb.my_bda; + + /* check whether there are any extensions to be forwarded */ + if (ext_type) + fw_ext_present = TRUE; + else + fw_ext_present = FALSE; + + if (bnep_cb.p_data_buf_cb) + { + (*bnep_cb.p_data_buf_cb)(p_bcb->handle, p_src_addr, p_dst_addr, protocol, p_buf, fw_ext_present); + } + else if (bnep_cb.p_data_ind_cb) + { + (*bnep_cb.p_data_ind_cb)(p_bcb->handle, p_src_addr, p_dst_addr, protocol, p, rem_len, fw_ext_present); + GKI_freebuf (p_buf); + } +} + + + +/******************************************************************************* +** +** Function bnep_process_timeout +** +** Description This function processes a timeout. If it is a startup +** timeout, we check for reading our BD address. If it +** is an L2CAP timeout, we send a disconnect req to L2CAP. +** +** Returns void +** +*******************************************************************************/ +void bnep_process_timeout (TIMER_LIST_ENT *p_tle) +{ + tBNEP_CONN *p_bcb; + + if (!p_tle->param) + { + if (!bnep_cb.got_my_bd_addr) + { + if (BTM_IsDeviceUp()) + BTM_ReadLocalDeviceAddr (bnep_read_addr_cb); + + btu_start_timer (&bnep_cb.bnep_tle, BTU_TTYPE_BNEP, 2); + } + return; + } + + p_bcb = (tBNEP_CONN *)p_tle->param; + + BNEP_TRACE_EVENT4 ("BNEP - CCB timeout in state: %d CID: 0x%x flags %x, re_transmit %d", + p_bcb->con_state, p_bcb->l2cap_cid, p_bcb->con_flags, p_bcb->re_transmits); + + if (p_bcb->con_state == BNEP_STATE_CONN_SETUP) + { + BNEP_TRACE_EVENT2 ("BNEP - CCB timeout in state: %d CID: 0x%x", + p_bcb->con_state, p_bcb->l2cap_cid); + + if (!(p_bcb->con_flags & BNEP_FLAGS_IS_ORIG)) + { + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + bnepu_release_bcb (p_bcb); + return; + } + + if (p_bcb->re_transmits++ != BNEP_MAX_RETRANSMITS) + { + bnep_send_conn_req (p_bcb); + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_CONN_TIMEOUT); + } + else + { + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + if ((p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) && (bnep_cb.p_conn_state_cb)) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_FAILED, FALSE); + + bnepu_release_bcb (p_bcb); + return; + } + } + else if (p_bcb->con_state != BNEP_STATE_CONNECTED) + { + BNEP_TRACE_EVENT2 ("BNEP - CCB timeout in state: %d CID: 0x%x", + p_bcb->con_state, p_bcb->l2cap_cid); + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + /* Tell the user if he has a callback */ + if ((p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) && (bnep_cb.p_conn_state_cb)) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_CONN_FAILED, FALSE); + + bnepu_release_bcb (p_bcb); + } +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + else if (p_bcb->con_flags & BNEP_FLAGS_FILTER_RESP_PEND) + { + if (p_bcb->re_transmits++ != BNEP_MAX_RETRANSMITS) + { + bnepu_send_peer_our_filters (p_bcb); + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_FILTER_SET_TIMEOUT); + } + else + { + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + /* Tell the user if he has a callback */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_SET_FILTER_FAIL, FALSE); + + bnepu_release_bcb (p_bcb); + return; + } + } +#endif +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + else if (p_bcb->con_flags & BNEP_FLAGS_MULTI_RESP_PEND) + { + if (p_bcb->re_transmits++ != BNEP_MAX_RETRANSMITS) + { + bnepu_send_peer_our_multi_filters (p_bcb); + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_FILTER_SET_TIMEOUT); + } + else + { + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + /* Tell the user if he has a callback */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_SET_FILTER_FAIL, FALSE); + + bnepu_release_bcb (p_bcb); + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function bnep_connected +** +** Description This function is called when a connection is established +** (after config). +** +** Returns void +** +*******************************************************************************/ +void bnep_connected (tBNEP_CONN *p_bcb) +{ + BOOLEAN is_role_change; + + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + is_role_change = TRUE; + else + is_role_change = FALSE; + + p_bcb->con_state = BNEP_STATE_CONNECTED; + p_bcb->con_flags |= BNEP_FLAGS_CONN_COMPLETED; + p_bcb->con_flags &= (~BNEP_FLAGS_SETUP_RCVD); + + /* Ensure timer is stopped */ + btu_stop_timer (&p_bcb->conn_tle); + p_bcb->re_transmits = 0; + + /* Tell the upper layer, if he has a callback */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_SUCCESS, is_role_change); +} + + +/******************************************************************************* +** +** Function bnep_read_addr_cb +** +** Description This function is called by BTM when the local BD address +** is read. It saves the BD address, and flags it as read. +** +** Returns void +** +*******************************************************************************/ +static void bnep_read_addr_cb (void *p_bda) +{ + UINT8 *bda = (UINT8 *)p_bda; + if (p_bda && + (bda[0] | bda[1] | bda[2] | bda[3] | bda[4] | bda[5]) != 0) + { + /* Save my BD address */ + memcpy (bnep_cb.my_bda, p_bda, BD_ADDR_LEN); + + bnep_cb.got_my_bd_addr = TRUE; + } + else + /* Retry after a couple seconds */ + btu_start_timer (&bnep_cb.bnep_tle, BTU_TTYPE_BNEP, 2); +} + diff --git a/stack/bnep/bnep_utils.c b/stack/bnep/bnep_utils.c new file mode 100644 index 0000000..205eeeb --- /dev/null +++ b/stack/bnep/bnep_utils.c @@ -0,0 +1,1463 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains BNEP utility functions + * + ******************************************************************************/ + +#include +#include +#include "gki.h" +#include "bt_types.h" +#include "bnep_int.h" +#include "btu.h" +#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 UINT8 *bnepu_init_hdr (BT_HDR *p_buf, UINT16 hdr_len, UINT8 pkt_type); + +void bnepu_process_peer_multicast_filter_set (tBNEP_CONN *p_bcb, UINT8 *p_filters, UINT16 len); +void bnepu_send_peer_multicast_filter_rsp (tBNEP_CONN *p_bcb, UINT16 response_code); + + +/******************************************************************************* +** +** Function bnepu_find_bcb_by_cid +** +** Description This function searches the bcb table for an entry with the +** passed CID. +** +** Returns the BCB address, or NULL if not found. +** +*******************************************************************************/ +tBNEP_CONN *bnepu_find_bcb_by_cid (UINT16 cid) +{ + UINT16 xx; + tBNEP_CONN *p_bcb; + + /* Look through each connection control block */ + for (xx = 0, p_bcb = bnep_cb.bcb; xx < BNEP_MAX_CONNECTIONS; xx++, p_bcb++) + { + if ((p_bcb->con_state != BNEP_STATE_IDLE) && (p_bcb->l2cap_cid == cid)) + return (p_bcb); + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function bnepu_find_bcb_by_bd_addr +** +** Description This function searches the BCB table for an entry with the +** passed Bluetooth Address. +** +** Returns the BCB address, or NULL if not found. +** +*******************************************************************************/ +tBNEP_CONN *bnepu_find_bcb_by_bd_addr (UINT8 *p_bda) +{ + UINT16 xx; + tBNEP_CONN *p_bcb; + + /* Look through each connection control block */ + for (xx = 0, p_bcb = bnep_cb.bcb; xx < BNEP_MAX_CONNECTIONS; xx++, p_bcb++) + { + if (p_bcb->con_state != BNEP_STATE_IDLE) + { + if (!memcmp ((UINT8 *)(p_bcb->rem_bda), p_bda, BD_ADDR_LEN)) + return (p_bcb); + } + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function bnepu_allocate_bcb +** +** Description This function allocates a new BCB. +** +** Returns BCB address, or NULL if none available. +** +*******************************************************************************/ +tBNEP_CONN *bnepu_allocate_bcb (BD_ADDR p_rem_bda) +{ + UINT16 xx; + tBNEP_CONN *p_bcb; + + /* Look through each connection control block for a free one */ + for (xx = 0, p_bcb = bnep_cb.bcb; xx < BNEP_MAX_CONNECTIONS; xx++, p_bcb++) + { + if (p_bcb->con_state == BNEP_STATE_IDLE) + { + memset ((UINT8 *)p_bcb, 0, sizeof (tBNEP_CONN)); + + p_bcb->conn_tle.param = (UINT32) p_bcb; + + memcpy ((UINT8 *)(p_bcb->rem_bda), (UINT8 *)p_rem_bda, BD_ADDR_LEN); + p_bcb->handle = xx + 1; + + return (p_bcb); + } + } + + /* If here, no free BCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function bnepu_release_bcb +** +** Description This function releases a BCB. +** +** Returns void +** +*******************************************************************************/ +void bnepu_release_bcb (tBNEP_CONN *p_bcb) +{ + /* Ensure timer is stopped */ + btu_stop_timer (&p_bcb->conn_tle); + + /* Drop any response pointer we may be holding */ + p_bcb->con_state = BNEP_STATE_IDLE; + p_bcb->p_pending_data = NULL; + + /* Free transmit queue */ + while (p_bcb->xmit_q.count) + { + GKI_freebuf (GKI_dequeue (&p_bcb->xmit_q)); + } +} + + +/******************************************************************************* +** +** Function bnep_send_conn_req +** +** Description This function sends a BNEP connection request to peer +** +** Returns void +** +*******************************************************************************/ +void bnep_send_conn_req (tBNEP_CONN *p_bcb) +{ + BT_HDR *p_buf; + UINT8 *p, *p_start; + + BNEP_TRACE_DEBUG1 ("BNEP sending setup req with dst uuid %x", p_bcb->dst_uuid.uu.uuid16); + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - not able to send connection request"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_SETUP_CONNECTION_REQUEST_MSG); + + UINT8_TO_BE_STREAM (p, p_bcb->dst_uuid.len); + + if (p_bcb->dst_uuid.len == 2) + { + UINT16_TO_BE_STREAM (p, p_bcb->dst_uuid.uu.uuid16); + UINT16_TO_BE_STREAM (p, p_bcb->src_uuid.uu.uuid16); + } +#if (defined (BNEP_SUPPORTS_ALL_UUID_LENGTHS) && BNEP_SUPPORTS_ALL_UUID_LENGTHS == TRUE) + else if (p_bcb->dst_uuid.len == 4) + { + UINT32_TO_BE_STREAM (p, p_bcb->dst_uuid.uu.uuid32); + UINT32_TO_BE_STREAM (p, p_bcb->src_uuid.uu.uuid32); + } + else + { + memcpy (p, p_bcb->dst_uuid.uu.uuid128, p_bcb->dst_uuid.len); + p += p_bcb->dst_uuid.len; + memcpy (p, p_bcb->src_uuid.uu.uuid128, p_bcb->dst_uuid.len); + p += p_bcb->dst_uuid.len; + } +#endif + + p_buf->len = (UINT16)(p - p_start); + + bnepu_check_send_packet (p_bcb, p_buf); +} + + +/******************************************************************************* +** +** Function bnep_send_conn_responce +** +** Description This function sends a BNEP setup response to peer +** +** Returns void +** +*******************************************************************************/ +void bnep_send_conn_responce (tBNEP_CONN *p_bcb, UINT16 resp_code) +{ + BT_HDR *p_buf; + UINT8 *p; + + BNEP_TRACE_EVENT1 ("BNEP - bnep_send_conn_responce for CID: 0x%x", p_bcb->l2cap_cid); + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - not able to send connection response"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_SETUP_CONNECTION_RESPONSE_MSG); + + UINT16_TO_BE_STREAM (p, resp_code); + + p_buf->len = 4; + + bnepu_check_send_packet (p_bcb, p_buf); + +} + + +/******************************************************************************* +** +** Function bnepu_send_peer_our_filters +** +** Description This function sends our filters to a peer +** +** Returns void +** +*******************************************************************************/ +void bnepu_send_peer_our_filters (tBNEP_CONN *p_bcb) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + BT_HDR *p_buf; + UINT8 *p; + UINT16 xx; + + BNEP_TRACE_DEBUG0 ("BNEP sending peer our filters"); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - no buffer send filters"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_FILTER_NET_TYPE_SET_MSG); + + UINT16_TO_BE_STREAM (p, (4 * p_bcb->sent_num_filters)); + for (xx = 0; xx < p_bcb->sent_num_filters; xx++) + { + UINT16_TO_BE_STREAM (p, p_bcb->sent_prot_filter_start[xx]); + UINT16_TO_BE_STREAM (p, p_bcb->sent_prot_filter_end[xx]); + } + + p_buf->len = 4 + (4 * p_bcb->sent_num_filters); + + bnepu_check_send_packet (p_bcb, p_buf); + + p_bcb->con_flags |= BNEP_FLAGS_FILTER_RESP_PEND; + + /* Start timer waiting for setup response */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_FILTER_SET_TIMEOUT); +#endif +} + + +/******************************************************************************* +** +** Function bnepu_send_peer_our_multi_filters +** +** Description This function sends our multicast filters to a peer +** +** Returns void +** +*******************************************************************************/ +void bnepu_send_peer_our_multi_filters (tBNEP_CONN *p_bcb) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + BT_HDR *p_buf; + UINT8 *p; + UINT16 xx; + + BNEP_TRACE_DEBUG0 ("BNEP sending peer our multicast filters"); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - no buffer to send multicast filters"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_FILTER_MULTI_ADDR_SET_MSG); + + UINT16_TO_BE_STREAM (p, (2 * BD_ADDR_LEN * p_bcb->sent_mcast_filters)); + for (xx = 0; xx < p_bcb->sent_mcast_filters; xx++) + { + memcpy (p, p_bcb->sent_mcast_filter_start[xx], BD_ADDR_LEN); + p += BD_ADDR_LEN; + memcpy (p, p_bcb->sent_mcast_filter_end[xx], BD_ADDR_LEN); + p += BD_ADDR_LEN; + } + + p_buf->len = 4 + (2 * BD_ADDR_LEN * p_bcb->sent_mcast_filters); + + bnepu_check_send_packet (p_bcb, p_buf); + + p_bcb->con_flags |= BNEP_FLAGS_MULTI_RESP_PEND; + + /* Start timer waiting for setup response */ + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_FILTER_SET_TIMEOUT); +#endif +} + + +/******************************************************************************* +** +** Function bnepu_send_peer_filter_rsp +** +** Description This function sends a filter response to a peer +** +** Returns void +** +*******************************************************************************/ +void bnepu_send_peer_filter_rsp (tBNEP_CONN *p_bcb, UINT16 response_code) +{ + BT_HDR *p_buf; + UINT8 *p; + + BNEP_TRACE_DEBUG0 ("BNEP sending filter response"); + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - no buffer filter rsp"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_FILTER_NET_TYPE_RESPONSE_MSG); + + UINT16_TO_BE_STREAM (p, response_code); + + p_buf->len = 4; + + bnepu_check_send_packet (p_bcb, p_buf); +} + + +/******************************************************************************* +** +** Function bnep_send_command_not_understood +** +** Description This function sends a BNEP command not understood message +** +** Returns void +** +*******************************************************************************/ +void bnep_send_command_not_understood (tBNEP_CONN *p_bcb, UINT8 cmd_code) +{ + BT_HDR *p_buf; + UINT8 *p; + + BNEP_TRACE_EVENT2 ("BNEP - bnep_send_command_not_understood for CID: 0x%x, cmd 0x%x", p_bcb->l2cap_cid, cmd_code); + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - not able to send connection response"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD); + + UINT8_TO_BE_STREAM (p, cmd_code); + + p_buf->len = 3; + + bnepu_check_send_packet (p_bcb, p_buf); + +} + + +/******************************************************************************* +** +** Function bnepu_check_send_packet +** +** Description This function tries to send a packet to L2CAP. +** If L2CAP is flow controlled, it enqueues the +** packet to the transmit queue +** +** Returns void +** +*******************************************************************************/ +void bnepu_check_send_packet (tBNEP_CONN *p_bcb, BT_HDR *p_buf) +{ + BNEP_TRACE_EVENT1 ("BNEP - bnepu_check_send_packet for CID: 0x%x", p_bcb->l2cap_cid); + if (p_bcb->con_flags & BNEP_FLAGS_L2CAP_CONGESTED) + { + if (p_bcb->xmit_q.count >= BNEP_MAX_XMITQ_DEPTH) + { + BNEP_TRACE_EVENT1 ("BNEP - congested, dropping buf, CID: 0x%x", p_bcb->l2cap_cid); + + GKI_freebuf (p_buf); + } + else + { + GKI_enqueue (&p_bcb->xmit_q, p_buf); + } + } + else + { + L2CA_DataWrite (p_bcb->l2cap_cid, p_buf); + } +} + + +/******************************************************************************* +** +** Function bnepu_build_bnep_hdr +** +** Description This function builds the BNEP header for a packet +** Extension headers are not sent yet, so there is no +** check for that. +** +** Returns void +** +*******************************************************************************/ +void bnepu_build_bnep_hdr (tBNEP_CONN *p_bcb, BT_HDR *p_buf, UINT16 protocol, + UINT8 *p_src_addr, UINT8 *p_dest_addr, BOOLEAN fw_ext_present) +{ + UINT8 ext_bit, *p = (UINT8 *)NULL; + UINT8 type = BNEP_FRAME_COMPRESSED_ETHERNET; + + ext_bit = fw_ext_present ? 0x80 : 0x00; + + if ((p_src_addr) && (memcmp (p_src_addr, bnep_cb.my_bda, BD_ADDR_LEN))) + type = BNEP_FRAME_COMPRESSED_ETHERNET_SRC_ONLY; + + if (memcmp (p_dest_addr, p_bcb->rem_bda, BD_ADDR_LEN)) + type = (type == BNEP_FRAME_COMPRESSED_ETHERNET) ? BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY : BNEP_FRAME_GENERAL_ETHERNET; + + if (!p_src_addr) + p_src_addr = (UINT8 *)bnep_cb.my_bda; + + switch (type) + { + case BNEP_FRAME_GENERAL_ETHERNET: + p = bnepu_init_hdr (p_buf, 15, (UINT8)(ext_bit | BNEP_FRAME_GENERAL_ETHERNET)); + + memcpy (p, p_dest_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + + memcpy (p, p_src_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + break; + + case BNEP_FRAME_COMPRESSED_ETHERNET: + p = bnepu_init_hdr (p_buf, 3, (UINT8)(ext_bit | BNEP_FRAME_COMPRESSED_ETHERNET)); + break; + + case BNEP_FRAME_COMPRESSED_ETHERNET_SRC_ONLY: + p = bnepu_init_hdr (p_buf, 9, (UINT8)(ext_bit | BNEP_FRAME_COMPRESSED_ETHERNET_SRC_ONLY)); + + memcpy (p, p_src_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + break; + + case BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY: + p = bnepu_init_hdr (p_buf, 9, (UINT8)(ext_bit | BNEP_FRAME_COMPRESSED_ETHERNET_DEST_ONLY)); + + memcpy (p, p_dest_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + break; + } + + UINT16_TO_BE_STREAM (p, protocol); +} + + +/******************************************************************************* +** +** Function bnepu_init_hdr +** +** Description This function initializes the BNEP header +** +** Returns pointer to header in buffer +** +*******************************************************************************/ +static UINT8 *bnepu_init_hdr (BT_HDR *p_buf, UINT16 hdr_len, UINT8 pkt_type) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* See if we need to make space in the buffer */ + if (p_buf->offset < (hdr_len + L2CAP_MIN_OFFSET)) + { + UINT16 xx, diff = BNEP_MINIMUM_OFFSET - p_buf->offset; + p = p + p_buf->len - 1; + for (xx = 0; xx < p_buf->len; xx++, p--) + p[diff] = *p; + + p_buf->offset = BNEP_MINIMUM_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + } + + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p -= hdr_len; + + *p++ = pkt_type; + + return (p); +} + + +/******************************************************************************* +** +** Function bnep_process_setup_conn_req +** +** Description This function processes a peer's setup connection request +** message. The destination UUID is verified and response sent +** Connection open indication will be given to PAN profile +** +** Returns void +** +*******************************************************************************/ +void bnep_process_setup_conn_req (tBNEP_CONN *p_bcb, UINT8 *p_setup, UINT8 len) +{ + BNEP_TRACE_EVENT1 ("BNEP - bnep_process_setup_conn_req for CID: 0x%x", p_bcb->l2cap_cid); + + if (p_bcb->con_state != BNEP_STATE_CONN_SETUP && + p_bcb->con_state != BNEP_STATE_SEC_CHECKING && + p_bcb->con_state != BNEP_STATE_CONNECTED) + { + BNEP_TRACE_ERROR1 ("BNEP - setup request in bad state %d", p_bcb->con_state); + bnep_send_conn_responce (p_bcb, BNEP_SETUP_CONN_NOT_ALLOWED); + return; + } + + /* Check if we already initiated security check or if waiting for user responce */ + if (p_bcb->con_flags & BNEP_FLAGS_SETUP_RCVD) + { + BNEP_TRACE_EVENT0 ("BNEP - Duplicate Setup message received while doing security check"); + return; + } + + /* Check if peer is the originator */ + if (p_bcb->con_state != BNEP_STATE_CONNECTED && + (!(p_bcb->con_flags & BNEP_FLAGS_SETUP_RCVD)) && + (p_bcb->con_flags & BNEP_FLAGS_IS_ORIG)) + { + BNEP_TRACE_ERROR1 ("BNEP - setup request when we are originator", p_bcb->con_state); + bnep_send_conn_responce (p_bcb, BNEP_SETUP_CONN_NOT_ALLOWED); + return; + } + + if (p_bcb->con_state == BNEP_STATE_CONNECTED) + { + memcpy ((UINT8 *)&(p_bcb->prv_src_uuid), (UINT8 *)&(p_bcb->src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->prv_dst_uuid), (UINT8 *)&(p_bcb->dst_uuid), sizeof (tBT_UUID)); + } + + p_bcb->dst_uuid.len = p_bcb->src_uuid.len = len; + + if (p_bcb->dst_uuid.len == 2) + { + /* because peer initiated connection keep src uuid as dst uuid */ + BE_STREAM_TO_UINT16 (p_bcb->src_uuid.uu.uuid16, p_setup); + BE_STREAM_TO_UINT16 (p_bcb->dst_uuid.uu.uuid16, p_setup); + + /* If nothing has changed don't bother the profile */ + if (p_bcb->con_state == BNEP_STATE_CONNECTED && + p_bcb->src_uuid.uu.uuid16 == p_bcb->prv_src_uuid.uu.uuid16 && + p_bcb->dst_uuid.uu.uuid16 == p_bcb->prv_dst_uuid.uu.uuid16) + { + bnep_send_conn_responce (p_bcb, BNEP_SETUP_CONN_OK); + return; + } + } +#if (defined (BNEP_SUPPORTS_ALL_UUID_LENGTHS) && BNEP_SUPPORTS_ALL_UUID_LENGTHS == TRUE) + else if (p_bcb->dst_uuid.len == 4) + { + BE_STREAM_TO_UINT32 (p_bcb->src_uuid.uu.uuid32, p_setup); + BE_STREAM_TO_UINT32 (p_bcb->dst_uuid.uu.uuid32, p_setup); + } + else if (p_bcb->dst_uuid.len == 16) + { + memcpy (p_bcb->src_uuid.uu.uuid128, p_setup, p_bcb->src_uuid.len); + p_setup += p_bcb->src_uuid.len; + memcpy (p_bcb->dst_uuid.uu.uuid128, p_setup, p_bcb->dst_uuid.len); + p_setup += p_bcb->dst_uuid.len; + } +#endif + else + { + BNEP_TRACE_ERROR1 ("BNEP - Bad UID len %d in ConnReq", p_bcb->dst_uuid.len); + bnep_send_conn_responce (p_bcb, BNEP_SETUP_INVALID_UUID_SIZE); + return; + } + + p_bcb->con_state = BNEP_STATE_SEC_CHECKING; + p_bcb->con_flags |= BNEP_FLAGS_SETUP_RCVD; + + BNEP_TRACE_EVENT1 ("BNEP initiating security check for incoming call for uuid 0x%x", p_bcb->src_uuid.uu.uuid16); +#if (!defined (BNEP_DO_AUTH_FOR_ROLE_SWITCH) || BNEP_DO_AUTH_FOR_ROLE_SWITCH == FALSE) + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + bnep_sec_check_complete (p_bcb->rem_bda, p_bcb, BTM_SUCCESS); + else +#endif + btm_sec_mx_access_request (p_bcb->rem_bda, BT_PSM_BNEP, FALSE, + BTM_SEC_PROTO_BNEP, bnep_get_uuid32(&(p_bcb->src_uuid)), + &bnep_sec_check_complete, p_bcb); + + return; +} + + +/******************************************************************************* +** +** Function bnep_process_setup_conn_responce +** +** Description This function processes a peer's setup connection response +** message. The response code is verified and +** Connection open indication will be given to PAN profile +** +** Returns void +** +*******************************************************************************/ +void bnep_process_setup_conn_responce (tBNEP_CONN *p_bcb, UINT8 *p_setup) +{ + tBNEP_RESULT resp; + UINT16 resp_code; + + BNEP_TRACE_DEBUG0 ("BNEP received setup responce"); + /* The state should be either SETUP or CONNECTED */ + if (p_bcb->con_state != BNEP_STATE_CONN_SETUP) + { + /* Should we disconnect ? */ + BNEP_TRACE_ERROR1 ("BNEP - setup response in bad state %d", p_bcb->con_state); + return; + } + + /* Check if we are the originator */ + if (!(p_bcb->con_flags & BNEP_FLAGS_IS_ORIG)) + { + BNEP_TRACE_ERROR1 ("BNEP - setup response when we are not originator", p_bcb->con_state); + return; + } + + BE_STREAM_TO_UINT16 (resp_code, p_setup); + + switch (resp_code) + { + case BNEP_SETUP_INVALID_SRC_UUID: + resp = BNEP_CONN_FAILED_SRC_UUID; + break; + + case BNEP_SETUP_INVALID_DEST_UUID: + resp = BNEP_CONN_FAILED_DST_UUID; + break; + + case BNEP_SETUP_INVALID_UUID_SIZE: + resp = BNEP_CONN_FAILED_UUID_SIZE; + break; + + case BNEP_SETUP_CONN_NOT_ALLOWED: + default: + resp = BNEP_CONN_FAILED; + break; + } + + /* Check the responce code */ + if (resp_code != BNEP_SETUP_CONN_OK) + { + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + { + BNEP_TRACE_EVENT1 ("BNEP - role change response is %d", resp_code); + + /* Restore the earlier BNEP status */ + p_bcb->con_state = BNEP_STATE_CONNECTED; + p_bcb->con_flags &= (~BNEP_FLAGS_SETUP_RCVD); + memcpy ((UINT8 *)&(p_bcb->src_uuid), (UINT8 *)&(p_bcb->prv_src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->dst_uuid), (UINT8 *)&(p_bcb->prv_dst_uuid), sizeof (tBT_UUID)); + + /* Ensure timer is stopped */ + btu_stop_timer (&p_bcb->conn_tle); + p_bcb->re_transmits = 0; + + /* Tell the user if he has a callback */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, resp, TRUE); + + return; + } + else + { + BNEP_TRACE_ERROR1 ("BNEP - setup response %d is not OK", resp_code); + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + /* Tell the user if he has a callback */ + if ((p_bcb->con_flags & BNEP_FLAGS_IS_ORIG) && (bnep_cb.p_conn_state_cb)) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, resp, FALSE); + + bnepu_release_bcb (p_bcb); + return; + } + } + + /* Received successful responce */ + bnep_connected (p_bcb); +} + + +/******************************************************************************* +** +** Function bnep_process_control_packet +** +** Description This function processes a peer's setup connection request +** message. The destination UUID is verified and response sent +** Connection open indication will be given to PAN profile +** +** Returns void +** +*******************************************************************************/ +UINT8 *bnep_process_control_packet (tBNEP_CONN *p_bcb, UINT8 *p, UINT16 *rem_len, BOOLEAN is_ext) +{ + UINT8 control_type; + BOOLEAN bad_pkt = FALSE; + UINT16 len, ext_len = 0; + + if (is_ext) + { + ext_len = *p++; + *rem_len = *rem_len - 1; + } + + control_type = *p++; + *rem_len = *rem_len - 1; + + BNEP_TRACE_EVENT3 ("BNEP processing control packet rem_len %d, is_ext %d, ctrl_type %d", *rem_len, is_ext, control_type); + + switch (control_type) + { + case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: + BNEP_TRACE_ERROR1 ("BNEP Received Cmd not understood for ctl pkt type: %d", *p); + p++; + *rem_len = *rem_len - 1; + break; + + case BNEP_SETUP_CONNECTION_REQUEST_MSG: + len = *p++; + if (*rem_len < ((2 * len) + 1)) + { + bad_pkt = TRUE; + BNEP_TRACE_ERROR0 ("BNEP Received Setup message with bad length"); + break; + } + if (!is_ext) + bnep_process_setup_conn_req (p_bcb, p, (UINT8)len); + p += (2 * len); + *rem_len = *rem_len - (2 * len) - 1; + break; + + case BNEP_SETUP_CONNECTION_RESPONSE_MSG: + if (!is_ext) + bnep_process_setup_conn_responce (p_bcb, p); + p += 2; + *rem_len = *rem_len - 2; + break; + + case BNEP_FILTER_NET_TYPE_SET_MSG: + BE_STREAM_TO_UINT16 (len, p); + if (*rem_len < (len + 2)) + { + bad_pkt = TRUE; + BNEP_TRACE_ERROR0 ("BNEP Received Filter set message with bad length"); + break; + } + bnepu_process_peer_filter_set (p_bcb, p, len); + p += len; + *rem_len = *rem_len - len - 2; + break; + + case BNEP_FILTER_NET_TYPE_RESPONSE_MSG: + bnepu_process_peer_filter_rsp (p_bcb, p); + p += 2; + *rem_len = *rem_len - 2; + break; + + case BNEP_FILTER_MULTI_ADDR_SET_MSG: + BE_STREAM_TO_UINT16 (len, p); + if (*rem_len < (len + 2)) + { + bad_pkt = TRUE; + BNEP_TRACE_ERROR0 ("BNEP Received Multicast Filter Set message with bad length"); + break; + } + bnepu_process_peer_multicast_filter_set (p_bcb, p, len); + p += len; + *rem_len = *rem_len - len - 2; + break; + + case BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG: + bnepu_process_multicast_filter_rsp (p_bcb, p); + p += 2; + *rem_len = *rem_len - 2; + break; + + default : + BNEP_TRACE_ERROR1 ("BNEP - bad ctl pkt type: %d", control_type); + bnep_send_command_not_understood (p_bcb, control_type); + if (is_ext) + { + p += (ext_len - 1); + *rem_len -= (ext_len - 1); + } + break; + } + + if (bad_pkt) + { + BNEP_TRACE_ERROR1 ("BNEP - bad ctl pkt length: %d", *rem_len); + *rem_len = 0; + return NULL; + } + + return p; +} + + +/******************************************************************************* +** +** Function bnepu_process_peer_filter_set +** +** Description This function processes a peer's filter control +** 'set' message. The filters are stored in the BCB, +** and an appropriate filter response message sent. +** +** Returns void +** +*******************************************************************************/ +void bnepu_process_peer_filter_set (tBNEP_CONN *p_bcb, UINT8 *p_filters, UINT16 len) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + UINT16 num_filters = 0; + UINT16 xx, resp_code = BNEP_FILTER_CRL_OK; + UINT16 start, end; + UINT8 *p_temp_filters; + + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + { + BNEP_TRACE_DEBUG0 ("BNEP received filter set from peer when there is no connection"); + return; + } + + BNEP_TRACE_DEBUG0 ("BNEP received filter set from peer"); + /* Check for length not a multiple of 4 */ + if (len & 3) + { + BNEP_TRACE_EVENT1 ("BNEP - bad filter len: %d", len); + bnepu_send_peer_filter_rsp (p_bcb, BNEP_FILTER_CRL_BAD_RANGE); + return; + } + + if (len) + num_filters = (UINT16) (len >> 2); + + /* Validate filter values */ + if (num_filters <= BNEP_MAX_PROT_FILTERS) + { + p_temp_filters = p_filters; + for (xx = 0; xx < num_filters; xx++) + { + BE_STREAM_TO_UINT16 (start, p_temp_filters); + BE_STREAM_TO_UINT16 (end, p_temp_filters); + + if (start > end) + { + resp_code = BNEP_FILTER_CRL_BAD_RANGE; + break; + } + } + } + else + resp_code = BNEP_FILTER_CRL_MAX_REACHED; + + if (resp_code != BNEP_FILTER_CRL_OK) + { + bnepu_send_peer_filter_rsp (p_bcb, resp_code); + return; + } + + if (bnep_cb.p_filter_ind_cb) + (*bnep_cb.p_filter_ind_cb) (p_bcb->handle, TRUE, 0, len, p_filters); + + p_bcb->rcvd_num_filters = num_filters; + for (xx = 0; xx < num_filters; xx++) + { + BE_STREAM_TO_UINT16 (start, p_filters); + BE_STREAM_TO_UINT16 (end, p_filters); + + p_bcb->rcvd_prot_filter_start[xx] = start; + p_bcb->rcvd_prot_filter_end[xx] = end; + } + + bnepu_send_peer_filter_rsp (p_bcb, resp_code); +#else + bnepu_send_peer_filter_rsp (p_bcb, BNEP_FILTER_CRL_UNSUPPORTED); +#endif +} + + +/******************************************************************************* +** +** Function bnepu_process_peer_filter_rsp +** +** Description This function processes a peer's filter control +** 'response' message. +** +** Returns void +** +*******************************************************************************/ +void bnepu_process_peer_filter_rsp (tBNEP_CONN *p_bcb, UINT8 *p_data) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + UINT16 resp_code; + tBNEP_RESULT result; + + BNEP_TRACE_DEBUG0 ("BNEP received filter responce"); + /* The state should be CONNECTED */ + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + { + BNEP_TRACE_ERROR1 ("BNEP - filter response in bad state %d", p_bcb->con_state); + return; + } + + /* Check if we are the originator */ + if (!(p_bcb->con_flags & BNEP_FLAGS_FILTER_RESP_PEND)) + { + BNEP_TRACE_ERROR0 ("BNEP - filter response when not expecting"); + return; + } + + /* Ensure timer is stopped */ + btu_stop_timer (&p_bcb->conn_tle); + p_bcb->con_flags &= ~BNEP_FLAGS_FILTER_RESP_PEND; + p_bcb->re_transmits = 0; + + BE_STREAM_TO_UINT16 (resp_code, p_data); + + result = BNEP_SUCCESS; + if (resp_code != BNEP_FILTER_CRL_OK) + result = BNEP_SET_FILTER_FAIL; + + if (bnep_cb.p_filter_ind_cb) + (*bnep_cb.p_filter_ind_cb) (p_bcb->handle, FALSE, result, 0, NULL); + + return; +#endif +} + + + +/******************************************************************************* +** +** Function bnepu_process_multicast_filter_rsp +** +** Description This function processes multicast filter control +** 'response' message. +** +** Returns void +** +*******************************************************************************/ +void bnepu_process_multicast_filter_rsp (tBNEP_CONN *p_bcb, UINT8 *p_data) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + UINT16 resp_code; + tBNEP_RESULT result; + + BNEP_TRACE_DEBUG0 ("BNEP received multicast filter responce"); + /* The state should be CONNECTED */ + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + { + BNEP_TRACE_ERROR1 ("BNEP - multicast filter response in bad state %d", p_bcb->con_state); + return; + } + + /* Check if we are the originator */ + if (!(p_bcb->con_flags & BNEP_FLAGS_MULTI_RESP_PEND)) + { + BNEP_TRACE_ERROR0 ("BNEP - multicast filter response when not expecting"); + return; + } + + /* Ensure timer is stopped */ + btu_stop_timer (&p_bcb->conn_tle); + p_bcb->con_flags &= ~BNEP_FLAGS_MULTI_RESP_PEND; + p_bcb->re_transmits = 0; + + BE_STREAM_TO_UINT16 (resp_code, p_data); + + result = BNEP_SUCCESS; + if (resp_code != BNEP_FILTER_CRL_OK) + result = BNEP_SET_FILTER_FAIL; + + if (bnep_cb.p_mfilter_ind_cb) + (*bnep_cb.p_mfilter_ind_cb) (p_bcb->handle, FALSE, result, 0, NULL); + + return; +#endif +} + + + +/******************************************************************************* +** +** Function bnepu_process_peer_multicast_filter_set +** +** Description This function processes a peer's filter control +** 'set' message. The filters are stored in the BCB, +** and an appropriate filter response message sent. +** +** Returns void +** +*******************************************************************************/ +void bnepu_process_peer_multicast_filter_set (tBNEP_CONN *p_bcb, UINT8 *p_filters, UINT16 len) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + UINT16 resp_code = BNEP_FILTER_CRL_OK; + UINT16 num_filters, xx; + UINT8 *p_temp_filters, null_bda[BD_ADDR_LEN] = {0,0,0,0,0,0}; + + if ((p_bcb->con_state != BNEP_STATE_CONNECTED) && + (!(p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED))) + { + BNEP_TRACE_DEBUG0 ("BNEP received multicast filter set from peer when there is no connection"); + return; + } + + if (len % 12) + { + BNEP_TRACE_EVENT1 ("BNEP - bad filter len: %d", len); + bnepu_send_peer_multicast_filter_rsp (p_bcb, BNEP_FILTER_CRL_BAD_RANGE); + return; + } + + if (len > (BNEP_MAX_MULTI_FILTERS * 2 * BD_ADDR_LEN)) + { + BNEP_TRACE_EVENT0 ("BNEP - Too many filters"); + bnepu_send_peer_multicast_filter_rsp (p_bcb, BNEP_FILTER_CRL_MAX_REACHED); + return; + } + + num_filters = 0; + if (len) + num_filters = (UINT16) (len / 12); + + /* Validate filter values */ + if (num_filters <= BNEP_MAX_MULTI_FILTERS) + { + p_temp_filters = p_filters; + for (xx = 0; xx < num_filters; xx++) + { + if (memcmp (p_temp_filters, p_temp_filters + BD_ADDR_LEN, BD_ADDR_LEN) > 0) + { + bnepu_send_peer_multicast_filter_rsp (p_bcb, BNEP_FILTER_CRL_BAD_RANGE); + return; + } + + p_temp_filters += (BD_ADDR_LEN * 2); + } + } + + p_bcb->rcvd_mcast_filters = num_filters; + for (xx = 0; xx < num_filters; xx++) + { + memcpy (p_bcb->rcvd_mcast_filter_start[xx], p_filters, BD_ADDR_LEN); + memcpy (p_bcb->rcvd_mcast_filter_end[xx], p_filters + BD_ADDR_LEN, BD_ADDR_LEN); + p_filters += (BD_ADDR_LEN * 2); + + /* Check if any of the ranges have all zeros as both starting and ending addresses */ + if ((memcmp (null_bda, p_bcb->rcvd_mcast_filter_start[xx], BD_ADDR_LEN) == 0) && + (memcmp (null_bda, p_bcb->rcvd_mcast_filter_end[xx], BD_ADDR_LEN) == 0)) + { + p_bcb->rcvd_mcast_filters = 0xFFFF; + break; + } + } + + BNEP_TRACE_EVENT1 ("BNEP multicast filters %d", p_bcb->rcvd_mcast_filters); + bnepu_send_peer_multicast_filter_rsp (p_bcb, resp_code); + + if (bnep_cb.p_mfilter_ind_cb) + (*bnep_cb.p_mfilter_ind_cb) (p_bcb->handle, TRUE, 0, len, p_filters); +#else + bnepu_send_peer_multicast_filter_rsp (p_bcb, BNEP_FILTER_CRL_UNSUPPORTED); +#endif +} + + +/******************************************************************************* +** +** Function bnepu_send_peer_multicast_filter_rsp +** +** Description This function sends a filter response to a peer +** +** Returns void +** +*******************************************************************************/ +void bnepu_send_peer_multicast_filter_rsp (tBNEP_CONN *p_bcb, UINT16 response_code) +{ + BT_HDR *p_buf; + UINT8 *p; + + BNEP_TRACE_DEBUG1 ("BNEP sending multicast filter response %d", response_code); + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (BNEP_POOL_ID)) == NULL) + { + BNEP_TRACE_ERROR0 ("BNEP - no buffer filter rsp"); + return; + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Put in BNEP frame type - filter control */ + UINT8_TO_BE_STREAM (p, BNEP_FRAME_CONTROL); + + /* Put in filter message type - set filters */ + UINT8_TO_BE_STREAM (p, BNEP_FILTER_MULTI_ADDR_RESPONSE_MSG); + + UINT16_TO_BE_STREAM (p, response_code); + + p_buf->len = 4; + + bnepu_check_send_packet (p_bcb, p_buf); +} + + + +/******************************************************************************* +** +** Function bnep_sec_check_complete +** +** Description This function is registered with BTM and will be called +** after completing the security procedures +** +** Returns void +** +*******************************************************************************/ +void bnep_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 result) +{ + tBNEP_CONN *p_bcb = (tBNEP_CONN *)p_ref_data; + UINT16 resp_code = BNEP_SETUP_CONN_OK; + BOOLEAN is_role_change; + + BNEP_TRACE_EVENT1 ("BNEP security callback returned result %d", result); + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + is_role_change = TRUE; + else + is_role_change = FALSE; + + /* check if the port is still waiting for security to complete */ + if (p_bcb->con_state != BNEP_STATE_SEC_CHECKING) + { + BNEP_TRACE_ERROR1 ("BNEP Connection in wrong state %d when security is completed", p_bcb->con_state); + return; + } + + /* if it is outgoing call and result is FAILURE return security fail error */ + if (!(p_bcb->con_flags & BNEP_FLAGS_SETUP_RCVD)) + { + if (result != BTM_SUCCESS) + { + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + { + /* Tell the user that role change is failed because of security */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_SECURITY_FAIL, is_role_change); + + p_bcb->con_state = BNEP_STATE_CONNECTED; + memcpy ((UINT8 *)&(p_bcb->src_uuid), (UINT8 *)&(p_bcb->prv_src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->dst_uuid), (UINT8 *)&(p_bcb->prv_dst_uuid), sizeof (tBT_UUID)); + return; + } + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + /* Tell the user if he has a callback */ + if (bnep_cb.p_conn_state_cb) + (*bnep_cb.p_conn_state_cb) (p_bcb->handle, p_bcb->rem_bda, BNEP_SECURITY_FAIL, is_role_change); + + bnepu_release_bcb (p_bcb); + return; + } + + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_bcb->con_state = BNEP_STATE_CONN_SETUP; + + bnep_send_conn_req (p_bcb); + btu_start_timer (&p_bcb->conn_tle, BTU_TTYPE_BNEP, BNEP_CONN_TIMEOUT); + return; + } + + /* it is an incoming call respond appropriately */ + if (result != BTM_SUCCESS) + { + bnep_send_conn_responce (p_bcb, BNEP_SETUP_CONN_NOT_ALLOWED); + if (p_bcb->con_flags & BNEP_FLAGS_CONN_COMPLETED) + { + /* Role change is failed because of security. Revert back to connected state */ + p_bcb->con_state = BNEP_STATE_CONNECTED; + p_bcb->con_flags &= (~BNEP_FLAGS_SETUP_RCVD); + memcpy ((UINT8 *)&(p_bcb->src_uuid), (UINT8 *)&(p_bcb->prv_src_uuid), sizeof (tBT_UUID)); + memcpy ((UINT8 *)&(p_bcb->dst_uuid), (UINT8 *)&(p_bcb->prv_dst_uuid), sizeof (tBT_UUID)); + return; + } + + L2CA_DisconnectReq (p_bcb->l2cap_cid); + + bnepu_release_bcb (p_bcb); + return; + } + + if (bnep_cb.p_conn_ind_cb) + { + p_bcb->con_state = BNEP_STATE_CONN_SETUP; + (*bnep_cb.p_conn_ind_cb) (p_bcb->handle, p_bcb->rem_bda, &p_bcb->dst_uuid, &p_bcb->src_uuid, is_role_change); + } + else + { + /* Profile didn't register connection indication call back */ + bnep_send_conn_responce (p_bcb, resp_code); + bnep_connected (p_bcb); + } + + return; +} + + +/******************************************************************************* +** +** Function bnep_is_packet_allowed +** +** Description This function verifies whether the protocol passes through +** the protocol filters set by the peer +** +** Returns BNEP_SUCCESS - if the protocol is allowed +** BNEP_IGNORE_CMD - if the protocol is filtered out +** +*******************************************************************************/ +tBNEP_RESULT bnep_is_packet_allowed (tBNEP_CONN *p_bcb, + BD_ADDR p_dest_addr, + UINT16 protocol, + BOOLEAN fw_ext_present, + UINT8 *p_data) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + if (p_bcb->rcvd_num_filters) + { + UINT16 i, proto; + + /* Findout the actual protocol to check for the filtering */ + proto = protocol; + if (proto == BNEP_802_1_P_PROTOCOL) + { + if (fw_ext_present) + { + UINT8 len, ext; + /* parse the extension headers and findout actual protocol */ + do { + + ext = *p_data++; + len = *p_data++; + p_data += len; + + } while (ext & 0x80); + } + p_data += 2; + BE_STREAM_TO_UINT16 (proto, p_data); + } + + for (i=0; ircvd_num_filters; i++) + { + if ((p_bcb->rcvd_prot_filter_start[i] <= proto) && + (proto <= p_bcb->rcvd_prot_filter_end[i])) + break; + } + + if (i == p_bcb->rcvd_num_filters) + { + BNEP_TRACE_DEBUG1 ("Ignoring protocol 0x%x in BNEP data write", proto); + return BNEP_IGNORE_CMD; + } + } +#endif + +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + /* Ckeck for multicast address filtering */ + if ((p_dest_addr[0] & 0x01) && + p_bcb->rcvd_mcast_filters) + { + UINT16 i; + + /* Check if every multicast should be filtered */ + if (p_bcb->rcvd_mcast_filters != 0xFFFF) + { + /* Check if the address is mentioned in the filter range */ + for (i = 0; i < p_bcb->rcvd_mcast_filters; i++) + { + if ((memcmp (p_bcb->rcvd_mcast_filter_start[i], p_dest_addr, BD_ADDR_LEN) <= 0) && + (memcmp (p_bcb->rcvd_mcast_filter_end[i], p_dest_addr, BD_ADDR_LEN) >= 0)) + break; + } + } + + /* + ** If every multicast should be filtered or the address is not in the filter range + ** drop the packet + */ + if ((p_bcb->rcvd_mcast_filters == 0xFFFF) || (i == p_bcb->rcvd_mcast_filters)) + { + BNEP_TRACE_DEBUG6 ("Ignoring multicast address %x.%x.%x.%x.%x.%x in BNEP data write", + p_dest_addr[0], p_dest_addr[1], p_dest_addr[2], + p_dest_addr[3], p_dest_addr[4], p_dest_addr[5]); + return BNEP_IGNORE_CMD; + } + } +#endif + + return BNEP_SUCCESS; +} + + + + + +/******************************************************************************* +** +** Function bnep_get_uuid32 +** +** Description This function returns the 32 bit equivalent of the given UUID +** +** Returns UINT32 - 32 bit equivalent of the UUID +** +*******************************************************************************/ +UINT32 bnep_get_uuid32 (tBT_UUID *src_uuid) +{ + UINT32 result; + + if (src_uuid->len == 2) + return ((UINT32)src_uuid->uu.uuid16); + else if (src_uuid->len == 4) + return (src_uuid->uu.uuid32 & 0x0000FFFF); + else + { + result = src_uuid->uu.uuid128[2]; + result = (result << 8) | (src_uuid->uu.uuid128[3]); + return result; + } +} + + + + +/******************************************************************************* +** +** Function bnep_dump_status +** +** Description This function dumps the bnep control block and connection +** blocks information +** +** Returns none +** +*******************************************************************************/ +void bnep_dump_status (void) +{ +#if (defined (BNEP_SUPPORTS_DEBUG_DUMP) && BNEP_SUPPORTS_DEBUG_DUMP == TRUE) + UINT16 i; + char buff[200]; + tBNEP_CONN *p_bcb; + + BNEP_TRACE_DEBUG6 ("BNEP my BD Addr %x.%x.%x.%x.%x.%x", + bnep_cb.my_bda[0], bnep_cb.my_bda[1], bnep_cb.my_bda[2], + bnep_cb.my_bda[3], bnep_cb.my_bda[4], bnep_cb.my_bda[5]); + BNEP_TRACE_DEBUG3 ("profile registered %d, trace %d, got_my_bd_addr %d", + bnep_cb.profile_registered, bnep_cb.trace_level, bnep_cb.got_my_bd_addr); + + for (i = 0, p_bcb = bnep_cb.bcb; i < BNEP_MAX_CONNECTIONS; i++, p_bcb++) + { + sprintf (buff, "%d state %d, flags 0x%x, cid %d, pfilts %d, mfilts %d, src 0x%x, dst 0x%x, BD %x.%x.%x.%x.%x.%x", + i, p_bcb->con_state, p_bcb->con_flags, p_bcb->l2cap_cid, + p_bcb->rcvd_num_filters, p_bcb->rcvd_mcast_filters, + p_bcb->src_uuid.uu.uuid16, p_bcb->dst_uuid.uu.uuid16, + p_bcb->rem_bda[0], p_bcb->rem_bda[1], p_bcb->rem_bda[2], + p_bcb->rem_bda[3], p_bcb->rem_bda[4], p_bcb->rem_bda[5]); + + BNEP_TRACE_DEBUG0 (buff); + } +#endif +} + + diff --git a/stack/btm/btm_acl.c b/stack/btm/btm_acl.c new file mode 100644 index 0000000..e3ac11b --- /dev/null +++ b/stack/btm/btm_acl.c @@ -0,0 +1,3141 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle ACL connections. This includes + * operations such as hold and sniff modes, supported packet types. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "bt_types.h" +#include "bt_target.h" +#include "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "hcidefs.h" +static void btm_establish_continue (tACL_CONN *p_acl_cb); + +#define BTM_DEV_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */ + +/******************************************************************************* +** +** Function btm_acl_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_acl_init (void) +{ + BTM_TRACE_DEBUG0 ("btm_acl_init"); +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.acl_db, 0, sizeof (btm_cb.acl_db)); +#if RFCOMM_INCLUDED == TRUE + memset (btm_cb.btm_scn, 0, BTM_MAX_SCN); /* Initialize the SCN usage to FALSE */ +#endif + btm_cb.btm_def_link_policy = 0; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_cb.p_bl_changed_cb = NULL; +#else + btm_cb.p_acl_changed_cb = NULL; +#endif +#endif + + /* Initialize nonzero defaults */ + btm_cb.btm_def_link_super_tout = HCI_DEFAULT_INACT_TOUT; + btm_cb.acl_disc_reason = 0xff ; +} + +/******************************************************************************* +** +** Function btm_bda_to_acl +** +** Description This function returns the FIRST acl_db entry for the passed BDA. +** +** Returns Returns pointer to the ACL DB for the requested BDA if found. +** NULL if not found. +** +*******************************************************************************/ +tACL_CONN *btm_bda_to_acl (BD_ADDR bda) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT16 xx; + if (bda) + { + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if ((p->in_use) && (!memcmp (p->remote_addr, bda, BD_ADDR_LEN))) + { + BTM_TRACE_DEBUG0 ("btm_bda_to_acl found"); + return(p); + } + } + } + BTM_TRACE_DEBUG0 ("btm_bda_to_acl Not found"); + + /* If here, no BD Addr found */ + return((tACL_CONN *)NULL); +} + +/******************************************************************************* +** +** Function btm_handle_to_acl_index +** +** Description This function returns the FIRST acl_db entry for the passed hci_handle. +** +** Returns index to the acl_db or MAX_L2CAP_LINKS. +** +*******************************************************************************/ +UINT8 btm_handle_to_acl_index (UINT16 hci_handle) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT8 xx; + BTM_TRACE_DEBUG0 ("btm_handle_to_acl_index"); + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if ((p->in_use) && (p->hci_handle == hci_handle)) + { + break; + } + } + + /* If here, no BD Addr found */ + return(xx); +} + + +/******************************************************************************* +** +** Function btm_acl_created +** +** Description This function is called by L2CAP when an ACL connection +** is created. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn, + UINT16 hci_handle, UINT8 link_role, UINT8 is_le_link) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 yy; + tACL_CONN *p; + UINT8 xx; + + BTM_TRACE_DEBUG3 ("btm_acl_created hci_handle=%d link_role=%d is_le_link=%d", + hci_handle,link_role, is_le_link); + /* Ensure we don't have duplicates */ + p = btm_bda_to_acl(bda); + if (p != (tACL_CONN *)NULL) + { + p->hci_handle = hci_handle; + p->link_role = link_role; +#if BLE_INCLUDED == TRUE + p->is_le_link = is_le_link; +#endif + BTM_TRACE_DEBUG6 ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy); + return; + } + + /* Allocate acl_db entry */ + for (xx = 0, p = &btm_cb.acl_db[0]; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if (!p->in_use) + { + p->in_use = TRUE; + p->hci_handle = hci_handle; + p->link_role = link_role; + p->link_up_issued = FALSE; +#if BLE_INCLUDED == TRUE + p->is_le_link = is_le_link; +#endif + p->restore_pkt_types = 0; /* Only exists while SCO is active */ + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + +#if BTM_PWR_MGR_INCLUDED == FALSE + p->mode = BTM_ACL_MODE_NORMAL; +#else + btm_pm_sm_alloc(xx); +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ + + memcpy (p->remote_addr, bda, BD_ADDR_LEN); + + if (dc) + memcpy (p->remote_dc, dc, DEV_CLASS_LEN); + + if (bdn) + memcpy (p->remote_name, bdn, BTM_MAX_REM_BD_NAME_LEN); + + + /* Check if we already know features for this device */ + p_dev_rec = btm_find_dev_by_handle (hci_handle); + +#if (BLE_INCLUDED == TRUE) + if (p_dev_rec ) + { + BTM_TRACE_DEBUG1 ("device_type=0x%x", p_dev_rec->device_type); + } +#endif + + + if (p_dev_rec +#if (BLE_INCLUDED == TRUE) + && p_dev_rec->device_type != BT_DEVICE_TYPE_BLE +#endif + ) + { + + /* if BR/EDR do something more */ + btsnd_hcic_read_rmt_clk_offset (p->hci_handle); + btsnd_hcic_rmt_ver_req (p->hci_handle); + + for (yy = 0; yy < BD_FEATURES_LEN; yy++) + { + if (p_dev_rec->features[yy]) + { + memcpy (p->features, p_dev_rec->features, BD_FEATURES_LEN); + if (BTM_SEC_MODE_SP == btm_cb.security_mode && + HCI_SIMPLE_PAIRING_SUPPORTED(p->features)) + { + /* if SM4 supported, check peer support for SM4 + * The remote controller supports SSP according to saved remote features + * read the extended feature page 1 for the host support for SSP */ + if (btsnd_hcic_rmt_ext_features (p_dev_rec->hci_handle, 1)) + return; + } + /* peer does not support SSP */ + p_dev_rec->sm4 |= BTM_SM4_KNOWN; + + btm_establish_continue (p); + return; + } + } + } +#if (BLE_INCLUDED == TRUE) + /* If here, features are not known yet */ + if (p_dev_rec && p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + btm_establish_continue(p); + + if (link_role == HCI_ROLE_MASTER) + { + btm_ble_update_bg_state(); + btm_ble_resume_bg_conn (NULL, FALSE); + + btsnd_hcic_ble_read_remote_feat(p->hci_handle); + } + } + else +#endif + { + btsnd_hcic_rmt_features_req (p->hci_handle); + } + + /* read page 1 - on rmt feature event for buffer reasons */ + return; + } + } +} + + +/******************************************************************************* +** +** Function btm_acl_report_role_change +** +** Description This function is called when the local device is deemed +** to be down. It notifies L2CAP of the failure. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_report_role_change (UINT8 hci_status, BD_ADDR bda) +{ + tBTM_ROLE_SWITCH_CMPL ref_data; + BTM_TRACE_DEBUG0 ("btm_acl_report_role_change"); + if (btm_cb.devcb.p_switch_role_cb && (bda && + (0 == memcmp(btm_cb.devcb.switch_role_ref_data.remote_bd_addr, bda, BD_ADDR_LEN)))) + { + memset (&btm_cb.devcb.switch_role_ref_data, 0, sizeof(tBTM_ROLE_SWITCH_CMPL)); + ref_data.hci_status = hci_status; + memcpy (&ref_data, &btm_cb.devcb.switch_role_ref_data, sizeof(tBTM_ROLE_SWITCH_CMPL)); + (*btm_cb.devcb.p_switch_role_cb)(&ref_data); + btm_cb.devcb.p_switch_role_cb = NULL; + } +} + +/******************************************************************************* +** +** Function btm_acl_removed +** +** Description This function is called by L2CAP when an ACL connection +** is removed. Since only L2CAP creates ACL links, we use +** the L2CAP link index as our index into the control blocks. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_removed (BD_ADDR bda) +{ + tACL_CONN *p; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_EVENT_DATA evt_data; +#endif +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec=NULL; + UINT16 combined_mode; +#endif + + BTM_TRACE_DEBUG0 ("btm_acl_removed"); + p = btm_bda_to_acl(bda); + if (p != (tACL_CONN *)NULL) + { + p->in_use = FALSE; + + /* if the disconnected channel has a pending role switch, clear it now */ + btm_acl_report_role_change(HCI_ERR_NO_CONNECTION, bda); + + /* Only notify if link up has had a chance to be issued */ + if (p->link_up_issued) + { + p->link_up_issued = FALSE; + + /* If anyone cares, tell him database changed */ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + if (btm_cb.p_bl_changed_cb) + { + evt_data.event = BTM_BL_DISCN_EVT; + evt_data.discn.p_bda = bda; + + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + + btm_acl_update_busy_level (BTM_BLI_ACL_DOWN_EVT); +#else + if (btm_cb.p_acl_changed_cb) + (*btm_cb.p_acl_changed_cb) (bda, NULL, NULL, NULL, FALSE); +#endif + } + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + + BTM_TRACE_DEBUG4 ("acl hci_handle=%d is_le_link=%d connectable_mode=0x%0x link_role=%d", + p->hci_handle, + p->is_le_link, + btm_cb.ble_ctr_cb.inq_var.connectable_mode, + p->link_role); + + + /* If we are LE connectable, check if we need to start advertising again */ + if ( p->is_le_link && (btm_cb.ble_ctr_cb.inq_var.connectable_mode != BTM_BLE_NON_CONNECTABLE) ) + { + tACL_CONN *pa = &btm_cb.acl_db[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, pa++) + { + /* If any other LE link is up, we are still not connectable */ + if (pa->in_use && pa->is_le_link) + return; + } + combined_mode = (btm_cb.ble_ctr_cb.inq_var.connectable_mode | btm_cb.btm_inq_vars.connectable_mode); + btm_ble_set_connectability ( combined_mode ); + } + + p_dev_rec = btm_find_dev(bda); + if ( p_dev_rec) + { + BTM_TRACE_DEBUG1("before update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags); + if (p->is_le_link) + { + BTM_TRACE_DEBUG0("LE link down"); + p_dev_rec->sec_flags &= ~(BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + if ( (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) == 0) + { + BTM_TRACE_DEBUG0("Not Bonded"); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHENTICATED | BTM_SEC_LINK_KEY_AUTHED); + } + else + { + BTM_TRACE_DEBUG0("Bonded"); + } + } + else + { + BTM_TRACE_DEBUG0("Bletooth link down"); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + } + BTM_TRACE_DEBUG1("after update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags); + } + else + { + BTM_TRACE_ERROR0("Device not found"); + + } +#endif + + return; + } +} + + +/******************************************************************************* +** +** Function btm_acl_device_down +** +** Description This function is called when the local device is deemed +** to be down. It notifies L2CAP of the failure. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_device_down (void) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT16 xx; + BTM_TRACE_DEBUG0 ("btm_acl_device_down"); + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if (p->in_use) + { + BTM_TRACE_DEBUG1 ("hci_handle=%d HCI_ERR_HW_FAILURE ",p->hci_handle ); + l2c_link_hci_disc_comp (p->hci_handle, HCI_ERR_HW_FAILURE); + } + } +} + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_acl_update_busy_level +** +** Description This function is called to update the busy level of the system +** . +** +** Returns void +** +*******************************************************************************/ +void btm_acl_update_busy_level (tBTM_BLI_EVENT event) +{ + tBTM_BL_UPDATE_DATA evt; + UINT8 busy_level; + BTM_TRACE_DEBUG0 ("btm_acl_update_busy_level"); + switch (event) + { + case BTM_BLI_ACL_UP_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_ACL_UP_EVT"); + btm_cb.num_acl++; + busy_level = (UINT8)btm_cb.num_acl; + break; + case BTM_BLI_ACL_DOWN_EVT: + if (btm_cb.num_acl) + { + btm_cb.num_acl--; + BTM_TRACE_DEBUG1 ("BTM_BLI_ACL_DOWN_EVT", btm_cb.num_acl); + } + else + { + BTM_TRACE_ERROR0 ("BTM_BLI_ACL_DOWN_EVT issued, but num_acl already zero !!!"); + } + busy_level = (UINT8)btm_cb.num_acl; + break; + case BTM_BLI_PAGE_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_PAGE_EVT"); + btm_cb.is_paging = TRUE; + busy_level = BTM_BL_PAGING_STARTED; + break; + case BTM_BLI_PAGE_DONE_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_PAGE_DONE_EVT"); + btm_cb.is_paging = FALSE; + busy_level = BTM_BL_PAGING_COMPLETE; + break; + case BTM_BLI_INQ_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_INQ_EVT"); + btm_cb.is_inquiry = TRUE; + busy_level = BTM_BL_INQUIRY_STARTED; + break; + case BTM_BLI_INQ_CANCEL_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_INQ_CANCEL_EVT"); + btm_cb.is_inquiry = FALSE; + busy_level = BTM_BL_INQUIRY_CANCELLED; + break; + case BTM_BLI_INQ_DONE_EVT: + BTM_TRACE_DEBUG0 ("BTM_BLI_INQ_DONE_EVT"); + btm_cb.is_inquiry = FALSE; + busy_level = BTM_BL_INQUIRY_COMPLETE; + break; + } + + if (busy_level != btm_cb.busy_level) + { + evt.event = BTM_BL_UPDATE_EVT; + evt.busy_level = busy_level; + btm_cb.busy_level = busy_level; + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_UPDATE_MASK)) + { + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + } + } +} +#endif + + +/******************************************************************************* +** +** Function BTM_GetRole +** +** Description This function is called to get the role of the local device +** for the ACL connection with the specified remote device +** +** Returns BTM_SUCCESS if connection exists. +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** +*******************************************************************************/ +tBTM_STATUS BTM_GetRole (BD_ADDR remote_bd_addr, UINT8 *p_role) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("BTM_GetRole"); + if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL) + { + *p_role = BTM_ROLE_UNDEFINED; + return(BTM_UNKNOWN_ADDR); + } + + /* Get the current role */ + *p_role = p->link_role; + return(BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SwitchRole +** +** Description This function is called to switch role between master and +** slave. If role is already set it will do nothing. If the +** command was initiated, the callback function is called upon +** completion. +** +** Returns BTM_SUCCESS if already in specified role. +** BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_MODE_UNSUPPORTED if local device does not support role switching +** BTM_BUSY if the previous command is not completed +** +*******************************************************************************/ +tBTM_STATUS BTM_SwitchRole (BD_ADDR remote_bd_addr, UINT8 new_role, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; +#if BTM_SCO_INCLUDED == TRUE + BOOLEAN is_sco_active; +#endif +#if BTM_PWR_MGR_INCLUDED == TRUE + tBTM_STATUS status; + tBTM_PM_MODE pwr_mode; + tBTM_PM_PWR_MD settings; +#endif +#if (BT_USE_TRACES == TRUE) + BD_ADDR_PTR p_bda; +#endif + BTM_TRACE_API6 ("BTM_SwitchRole BDA: %02x-%02x-%02x-%02x-%02x-%02x", + remote_bd_addr[0], remote_bd_addr[1], remote_bd_addr[2], + remote_bd_addr[3], remote_bd_addr[4], remote_bd_addr[5]); + + /* Make sure the local device supports switching */ + if (!(HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features))) + return(BTM_MODE_UNSUPPORTED); + + if (btm_cb.devcb.p_switch_role_cb && p_cb) + { +#if (BT_USE_TRACES == TRUE) + p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + BTM_TRACE_DEBUG6 ("Role switch on other device is in progress 0x%02x%02x%02x%02x%02x%02x", + p_bda[0], p_bda[1], p_bda[2], + p_bda[3], p_bda[4], p_bda[5]); +#endif + return(BTM_BUSY); + } + + if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL) + return(BTM_UNKNOWN_ADDR); + + /* Finished if already in desired role */ + if (p->link_role == new_role) + return(BTM_SUCCESS); + +#if BTM_SCO_INCLUDED == TRUE + /* Check if there is any SCO Active on this BD Address */ + is_sco_active = btm_is_sco_active_by_bdaddr(remote_bd_addr); + + if (is_sco_active == TRUE) + return(BTM_NO_RESOURCES); +#endif + + /* Ignore role switch request if the previous request was not completed */ + if (p->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) + { + BTM_TRACE_DEBUG1 ("BTM_SwitchRole busy: %d", + p->switch_role_state); + return(BTM_BUSY); + } + + /* Cannot switch role while parked or sniffing */ +#if BTM_PWR_MGR_INCLUDED == FALSE + if (p->mode == HCI_MODE_PARK) + { + if (!btsnd_hcic_exit_park_mode (p->hci_handle)) + return(BTM_NO_RESOURCES); + + p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } + else if (p->mode == HCI_MODE_SNIFF) + { + if (!btsnd_hcic_exit_sniff_mode (p->hci_handle)) + return(BTM_NO_RESOURCES); + + p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } +#else /* power manager is in use */ + + if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS) + return(status); + + /* Wake up the link if in sniff or park before attempting switch */ + if (pwr_mode == BTM_PM_MD_PARK || pwr_mode == BTM_PM_MD_SNIFF) + { +/* Coverity FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode + the other data members of tBTM_PM_PWR_MD are ignored +*/ + settings.mode = BTM_PM_MD_ACTIVE; + status = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p->remote_addr, &settings); + if (status != BTM_CMD_STARTED) + return(BTM_WRONG_MODE); + + p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } +#endif + /* some devices do not support switch while encryption is on */ + else + { + if (((p_dev_rec = btm_find_dev (remote_bd_addr)) != NULL) + && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) + && !BTM_EPR_AVAILABLE(p)) + { + /* bypass turning off encryption if change link key is already doing it */ + if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF) + { + if (!btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE)) + return(BTM_NO_RESOURCES); + else + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF; + } + + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + } + else + { + if (!btsnd_hcic_switch_role (remote_bd_addr, new_role)) + return(BTM_NO_RESOURCES); + + p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; + +#if BTM_DISC_DURING_RS == TRUE + if (p_dev_rec) + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; +#endif + } + } + + /* Initialize return structure in case request fails */ + if (p_cb) + { + memcpy (btm_cb.devcb.switch_role_ref_data.remote_bd_addr, remote_bd_addr, + BD_ADDR_LEN); + btm_cb.devcb.switch_role_ref_data.role = new_role; + /* initialized to an error code */ + btm_cb.devcb.switch_role_ref_data.hci_status = HCI_ERR_UNSUPPORTED_VALUE; + btm_cb.devcb.p_switch_role_cb = p_cb; + } + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function BTM_ChangeLinkKey +** +** Description This function is called to change the link key of the +** connection. +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if the previous command is not completed +** +*******************************************************************************/ +tBTM_STATUS BTM_ChangeLinkKey (BD_ADDR remote_bd_addr, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; +#if BTM_PWR_MGR_INCLUDED == TRUE + tBTM_STATUS status; + tBTM_PM_MODE pwr_mode; + tBTM_PM_PWR_MD settings; +#endif + BTM_TRACE_DEBUG0 ("BTM_ChangeLinkKey"); + if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL) + return(BTM_UNKNOWN_ADDR); + + /* Ignore change link key request if the previsous request has not completed */ + if (p->change_key_state != BTM_ACL_SWKEY_STATE_IDLE) + { + BTM_TRACE_DEBUG0 ("Link key change request declined since the previous request for this device has not completed "); + return(BTM_BUSY); + } + + memset (&btm_cb.devcb.chg_link_key_ref_data, 0, sizeof(tBTM_CHANGE_KEY_CMPL)); + + /* Cannot change key while parked */ +#if BTM_PWR_MGR_INCLUDED == FALSE + if (p->mode == HCI_MODE_PARK) + { + if (!btsnd_hcic_exit_park_mode (p->hci_handle)) + return(BTM_NO_RESOURCES); + + p->change_key_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } +#else /* power manager is in use */ + + + if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS) + return(status); + + /* Wake up the link if in park before attempting to change link keys */ + if (pwr_mode == BTM_PM_MD_PARK) + { +/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode + the other data members of tBTM_PM_PWR_MD are ignored +*/ + settings.mode = BTM_PM_MD_ACTIVE; + status = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p->remote_addr, &settings); + if (status != BTM_CMD_STARTED) + return(BTM_WRONG_MODE); + + p->change_key_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE; + } +#endif + /* some devices do not support change of link key while encryption is on */ + else if (((p_dev_rec = btm_find_dev (remote_bd_addr)) != NULL) + && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) && !BTM_EPR_AVAILABLE(p)) + { + /* bypass turning off encryption if switch role is already doing it */ + if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF) + { + if (!btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE)) + return(BTM_NO_RESOURCES); + else + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF; + } + + p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + } + else /* Ok to initiate change of link key */ + { + if (!btsnd_hcic_change_link_key (p->hci_handle)) + return(BTM_NO_RESOURCES); + + p->change_key_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; + } + + /* Initialize return structure in case request fails */ + memcpy (btm_cb.devcb.chg_link_key_ref_data.remote_bd_addr, remote_bd_addr, + BD_ADDR_LEN); + btm_cb.devcb.p_chg_link_key_cb = p_cb; + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_acl_link_key_change +** +** Description This function is called to when a change link key event +** is received. +** +*******************************************************************************/ +void btm_acl_link_key_change (UINT16 handle, UINT8 status) +{ + tBTM_CHANGE_KEY_CMPL *p_data; + tACL_CONN *p; + UINT8 xx; + BTM_TRACE_DEBUG0 ("btm_acl_link_key_change"); + /* Look up the connection by handle and set the current mode */ + xx = btm_handle_to_acl_index(handle); + + /* don't assume that we can never get a bad hci_handle */ + if (xx >= MAX_L2CAP_LINKS) + return; + + p_data = &btm_cb.devcb.chg_link_key_ref_data; + p = &btm_cb.acl_db[xx]; + p_data->hci_status = status; + + /* if switching state is switching we need to turn encryption on */ + /* if idle, we did not change encryption */ + if (p->change_key_state == BTM_ACL_SWKEY_STATE_SWITCHING) + { + /* Make sure there's not also a role switch going on before re-enabling */ + if (p->switch_role_state != BTM_ACL_SWKEY_STATE_SWITCHING) + { + if (btsnd_hcic_set_conn_encrypt (p->hci_handle, TRUE)) + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON; + p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON; + return; + } + } + else /* Set the state and wait for change link key */ + { + p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON; + return; + } + } + + /* Set the switch_role_state to IDLE since the reply received from HCI */ + /* regardless of its result either success or failed. */ + if (p->change_key_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS) + { + p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } + + if (btm_cb.devcb.p_chg_link_key_cb) + { + (*btm_cb.devcb.p_chg_link_key_cb)((void *)p_data); + btm_cb.devcb.p_chg_link_key_cb = NULL; + } + + BTM_TRACE_ERROR2("Change Link Key Complete Event: Handle 0x%02x, HCI Status 0x%02x", + handle, p_data->hci_status); +} + +/******************************************************************************* +** +** Function btm_acl_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM. Checks to see if a role switch or +** change of link key was active and initiates or continues +** process if needed. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) +{ + tACL_CONN *p; + UINT8 xx; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_ROLE_CHG_DATA evt; +#endif +#if BTM_DISC_DURING_RS == TRUE + tBTM_SEC_DEV_REC *p_dev_rec; +#endif + BTM_TRACE_DEBUG3 ("btm_acl_encrypt_change handle=%d status=%d encr_enabl=%d", handle, status, encr_enable); + xx = btm_handle_to_acl_index(handle); + /* don't assume that we can never get a bad hci_handle */ + if (xx < MAX_L2CAP_LINKS) + p = &btm_cb.acl_db[xx]; + else + return; + + /* Process Role Switch if active */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF) + { + /* if encryption turn off failed we still will try to switch role */ + if (encr_enable) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } + else + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_SWITCHING; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC; + } + + if (!btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role)) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr); + } +#if BTM_DISC_DURING_RS == TRUE + else + { + if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL) + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; + } +#endif + + } + /* Finished enabling Encryption after role switch */ + else if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr); + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + /* if role change event is registered, report it now */ + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) + { + evt.event = BTM_BL_ROLE_CHG_EVT; + evt.new_role = btm_cb.devcb.switch_role_ref_data.role; + evt.p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + evt.hci_status = btm_cb.devcb.switch_role_ref_data.hci_status; + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + + BTM_TRACE_DEBUG3("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d", + evt.new_role, evt.hci_status, p->switch_role_state); + } +#endif + +#if BTM_DISC_DURING_RS == TRUE + /* If a disconnect is pending, issue it now that role switch has completed */ + if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL) + { + if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) + { + BTM_TRACE_WARNING0("btm_acl_encrypt_change -> Issuing delayed HCI_Disconnect!!!"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_ERROR2("btm_acl_encrypt_change: tBTM_SEC_DEV:0x%x rs_disc_pending=%d", + (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } +#endif + } + + + /* Process Change Link Key if active */ + if (p->change_key_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF) + { + /* if encryption turn off failed we still will try to change link key */ + if (encr_enable) + { + p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } + else + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC; + p->change_key_state = BTM_ACL_SWKEY_STATE_SWITCHING; + } + + if (!btsnd_hcic_change_link_key (p->hci_handle)) + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE; + if (btm_cb.devcb.p_chg_link_key_cb) + { + (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data); + btm_cb.devcb.p_chg_link_key_cb = NULL; + } + } + } + /* Finished enabling Encryption after changing link key */ + else if (p->change_key_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON) + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE; + if (btm_cb.devcb.p_chg_link_key_cb) + { + (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data); + btm_cb.devcb.p_chg_link_key_cb = NULL; + } + } +} +/******************************************************************************* +** +** Function BTM_SetLinkPolicy +** +** Description Create and send HCI "Write Policy Set" command +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLinkPolicy (BD_ADDR remote_bda, UINT16 *settings) +{ + tACL_CONN *p; + UINT8 *localFeatures = BTM_ReadLocalFeatures(); + BTM_TRACE_DEBUG0 ("BTM_SetLinkPolicy"); +/* BTM_TRACE_API1 ("BTM_SetLinkPolicy: requested settings: 0x%04x", *settings ); */ + + /* First, check if hold mode is supported */ + if (*settings != HCI_DISABLE_ALL_LM_MODES) + { + if ( (*settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) && (!HCI_SWITCH_SUPPORTED(localFeatures)) ) + { + *settings &= (~HCI_ENABLE_MASTER_SLAVE_SWITCH); + BTM_TRACE_API1 ("BTM_SetLinkPolicy switch not supported (settings: 0x%04x)", *settings ); + } + if ( (*settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures)) ) + { + *settings &= (~HCI_ENABLE_HOLD_MODE); + BTM_TRACE_API1 ("BTM_SetLinkPolicy hold not supported (settings: 0x%04x)", *settings ); + } + if ( (*settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) ) + { + *settings &= (~HCI_ENABLE_SNIFF_MODE); + BTM_TRACE_API1 ("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)", *settings ); + } + if ( (*settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures)) ) + { + *settings &= (~HCI_ENABLE_PARK_MODE); + BTM_TRACE_API1 ("BTM_SetLinkPolicy park not supported (settings: 0x%04x)", *settings ); + } + } + + if ((p = btm_bda_to_acl(remote_bda)) != NULL) + return(btsnd_hcic_write_policy_set (p->hci_handle, *settings) ? BTM_CMD_STARTED : BTM_NO_RESOURCES); + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkPolicy +** +** Description Set the default value for HCI "Write Policy Set" command +** to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetDefaultLinkPolicy (UINT16 settings) +{ + BTM_TRACE_DEBUG0 ("BTM_SetDefaultLinkPolicy"); + btm_cb.btm_def_link_policy = settings; +} + + +/******************************************************************************* +** +** Function BTM_ReadLinkPolicy +** +** Description This function is called to read the link policy settings. +** The address of link policy results are returned in the callback. +** (tBTM_LNK_POLICY_RESULTS) +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLinkPolicy (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadLinkPolicy: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_rlinkp_cmpl_cb) + return(BTM_BUSY); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + btu_start_timer (&btm_cb.devcb.rlinkp_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + btm_cb.devcb.p_rlinkp_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_policy_set (p->hci_handle)) + { + btu_stop_timer (&btm_cb.devcb.rlinkp_timer); + btm_cb.devcb.p_rlinkp_cmpl_cb = NULL; + return(BTM_NO_RESOURCES); + } + + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function btm_read_link_policy_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read local link policy request. +** +** Returns void +** +*******************************************************************************/ +void btm_read_link_policy_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb; + tBTM_LNK_POLICY_RESULTS lnkpol; + UINT16 handle; + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT16 index; + BTM_TRACE_DEBUG0 ("btm_read_link_policy_complete"); + btu_stop_timer (&btm_cb.devcb.rlinkp_timer); + + /* If there was a callback address for read local version, call it */ + btm_cb.devcb.p_rlinkp_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (lnkpol.hci_status, p); + + if (lnkpol.hci_status == HCI_SUCCESS) + { + lnkpol.status = BTM_SUCCESS; + + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT16 (lnkpol.settings, p); + + /* Search through the list of active channels for the correct BD Addr */ + for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) + { + memcpy (lnkpol.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + break; + } + } + } + else + lnkpol.status = BTM_ERR_PROCESSING; + + (*p_cb)(&lnkpol); + } +} + + +/******************************************************************************* +** +** Function btm_read_remote_version_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the remote version info. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_version_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT8 status; + UINT16 handle; + int xx; + BTM_TRACE_DEBUG0 ("btm_read_remote_version_complete"); + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle)) + { + STREAM_TO_UINT8 (p_acl_cb->lmp_version, p); + STREAM_TO_UINT16 (p_acl_cb->manufacturer, p); + STREAM_TO_UINT16 (p_acl_cb->lmp_subversion, p); + break; + } + } + } +} + + +/******************************************************************************* +** +** Function btm_read_remote_features_complete +** +** Description This function is called when the remote extended features +** complete event is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_features_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT8 status; + UINT16 handle; + int xx, yy; + UINT8 req_pend; + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_DEBUG0 ("btm_read_remote_features_complete"); + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle)) + { + for (yy = 0; yy < BD_FEATURES_LEN; yy++) + STREAM_TO_UINT8 (p_acl_cb->features[yy], p); + + p_dev_rec = btm_find_dev_by_handle (handle); + if (!p_dev_rec) + { + /* Get a new device; might be doing dedicated bonding */ + p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr); + } + + memcpy (p_dev_rec->features, p_acl_cb->features, BD_FEATURES_LEN); + + if (BTM_SEC_MODE_SP == btm_cb.security_mode && + HCI_SIMPLE_PAIRING_SUPPORTED(p_acl_cb->features)) + { + /* if SM4 supported, check peer support for SM4 + * The remote controller supports SSP + * read the extended feature page 1 for the host support for SSP */ + if (btsnd_hcic_rmt_ext_features (handle, 1)) + break; + } + else + { + req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); + p_dev_rec->sm4 = BTM_SM4_KNOWN; + if (req_pend) + { + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + } + } + + btm_establish_continue (p_acl_cb); + break; + } + } + } +} + +/******************************************************************************* +** +** Function btm_read_remote_ext_features_complete +** +** Description This function is called when the remote extended features +** complete event is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 status, page_num, max_page; + UINT16 handle; + int xx; + BD_FEATURES ext_features; /* extended Features suported by the device */ + UINT8 req_pend; + BTM_TRACE_DEBUG0 ("btm_read_remote_ext_features_complete"); + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle)) + { + STREAM_TO_UINT8 (page_num, p); + STREAM_TO_UINT8 (max_page, p); + p_dev_rec = btm_find_dev_by_handle (handle); + if (!p_dev_rec) + p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr); + + req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); + + if (page_num == 1 && max_page >= 1) + { + /* only the byte 0 of page 1 is used right now */ + STREAM_TO_UINT8 (ext_features[0], p); + + if (HCI_SSP_HOST_SUPPORTED(ext_features)) + { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } + } + + BTM_TRACE_API5 ("ext_features_complt page_num:%d max_page:%d f[0]:x%02x, sm4:%x, pend:%d", + page_num, max_page, *p, p_dev_rec->sm4, req_pend); + + if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + } + + if (req_pend) + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + + btm_establish_continue (p_acl_cb); + + break; + } + } + } +} + +/******************************************************************************* +** +** Function btm_read_remote_ext_features_failed +** +** Description This function is called when the remote extended features +** complete event returns a failed status. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features_failed (UINT8 status) +{ + BTM_TRACE_ERROR1 ("btm_read_remote_ext_features_failed (status 0x%02x)", status); +} + +/******************************************************************************* +** +** Function btm_establish_continue +** +** Description This function is called when the command complete message +** is received from the HCI for the read local link policy request. +** +** Returns void +** +*******************************************************************************/ +static void btm_establish_continue (tACL_CONN *p_acl_cb) +{ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_EVENT_DATA evt_data; +#endif + BTM_TRACE_DEBUG0 ("btm_establish_continue"); +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + if (!p_acl_cb->is_le_link) +#endif + { + /* For now there are a some devices that do not like sending */ + /* commands events and data at the same time. */ + /* Set the packet types to the default allowed by the device */ + btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); + + if (btm_cb.btm_def_link_policy) + BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); + + BTM_SetLinkSuperTout (p_acl_cb->remote_addr, btm_cb.btm_def_link_super_tout); + } +#endif + p_acl_cb->link_up_issued = TRUE; + + /* If anyone cares, tell him database changed */ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + if (btm_cb.p_bl_changed_cb) + { + evt_data.event = BTM_BL_CONN_EVT; + evt_data.conn.p_bda = p_acl_cb->remote_addr; + evt_data.conn.p_bdn = p_acl_cb->remote_name; + evt_data.conn.p_dc = p_acl_cb->remote_dc; + evt_data.conn.p_features = p_acl_cb->features; + + + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + btm_acl_update_busy_level (BTM_BLI_ACL_UP_EVT); +#else + if (btm_cb.p_acl_changed_cb) + (*btm_cb.p_acl_changed_cb) (p_acl_cb->remote_addr, + p_acl_cb->remote_dc, + p_acl_cb->remote_name, + p_acl_cb->features, + TRUE); +#endif +} + + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkSuperTout +** +** Description Set the default value for HCI "Write Link Supervision Timeout" +** command to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetDefaultLinkSuperTout (UINT16 timeout) +{ + BTM_TRACE_DEBUG0 ("BTM_SetDefaultLinkSuperTout"); + btm_cb.btm_def_link_super_tout = timeout; +} + +/******************************************************************************* +** +** Function BTM_SetLinkSuperTout +** +** Description Create and send HCI "Write Link Supervision Timeout" command +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLinkSuperTout (BD_ADDR remote_bda, UINT16 timeout) +{ + tACL_CONN *p = btm_bda_to_acl(remote_bda); + + BTM_TRACE_DEBUG0 ("BTM_SetLinkSuperTout"); + if (p != (tACL_CONN *)NULL) + { + p->link_super_tout = timeout; + + /* Only send if current role is Master; 2.0 spec requires this */ + if (p->link_role == BTM_ROLE_MASTER) + { + if (!btsnd_hcic_write_link_super_tout (LOCAL_BR_EDR_CONTROLLER_ID, + p->hci_handle, timeout)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); + } + else + return(BTM_SUCCESS); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_RegForLstoEvt +** +** Description register for the HCI "Link Supervision Timeout Change" event +** +** Returns void +** +*******************************************************************************/ +void BTM_RegForLstoEvt (tBTM_LSTO_CBACK *p_cback) +{ + BTM_TRACE_DEBUG0 ("BTM_RegForLstoEvt"); + btm_cb.p_lsto_cback = p_cback; +} + +/******************************************************************************* +** +** Function btm_proc_lsto_evt +** +** Description process the HCI "Link Supervision Timeout Change" event +** +** Returns void +** +*******************************************************************************/ +void btm_proc_lsto_evt(UINT16 handle, UINT16 timeout) +{ + UINT8 xx; + + BTM_TRACE_DEBUG0 ("btm_proc_lsto_evt"); + if (btm_cb.p_lsto_cback) + { + /* Look up the connection by handle and set the current mode */ + xx = btm_handle_to_acl_index(handle); + + /* don't assume that we can never get a bad hci_handle */ + if (xx < MAX_L2CAP_LINKS) + { + (*btm_cb.p_lsto_cback)(btm_cb.acl_db[xx].remote_addr, timeout); + } + } +} + +#if BTM_PWR_MGR_INCLUDED == FALSE +/******************************************************************************* +** +** Function BTM_SetHoldMode +** +** Description This function is called to set a connection into hold mode. +** A check is made if the connection is in sniff or park mode, +** and if yes, the hold mode is ignored. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetHoldMode (BD_ADDR remote_bda, UINT16 min_interval, UINT16 max_interval) +{ + tACL_CONN *p; + + BTM_TRACE_DEBUG0 ("BTM_SetHoldMode"); + /* First, check if hold mode is supported */ + if (!HCI_HOLD_MODE_SUPPORTED(BTM_ReadLocalFeatures())) + return(BTM_MODE_UNSUPPORTED); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + /* If the connection is in park or sniff mode, forget about holding it */ + if (p->mode != BTM_ACL_MODE_NORMAL) + return(BTM_SUCCESS); + + if (!btsnd_hcic_hold_mode (p->hci_handle, max_interval, min_interval)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function BTM_SetSniffMode +** +** Description This function is called to set a connection into sniff mode. +** A check is made if the connection is already in sniff or park +** mode, and if yes, the sniff mode is ignored. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetSniffMode (BD_ADDR remote_bda, UINT16 min_period, UINT16 max_period, + UINT16 attempt, UINT16 timeout) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("BTM_SetSniffMode"); + /* First, check if sniff mode is supported */ + if (!HCI_SNIFF_MODE_SUPPORTED(BTM_ReadLocalFeatures())) + return(BTM_MODE_UNSUPPORTED); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + /* If the connection is in park mode, forget about sniffing it */ + if (p->mode != BTM_ACL_MODE_NORMAL) + return(BTM_WRONG_MODE); + + if (!btsnd_hcic_sniff_mode (p->hci_handle, max_period, + min_period, attempt, timeout)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + + + + +/******************************************************************************* +** +** Function BTM_CancelSniffMode +** +** Description This function is called to put a connection out of sniff mode. +** A check is made if the connection is already in sniff mode, +** and if not, the cancel sniff mode is ignored. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelSniffMode (BD_ADDR remote_bda) +{ + tACL_CONN *p = btm_bda_to_acl(remote_bda); + BTM_TRACE_DEBUG0 ("BTM_CancelSniffMode "); + if (p == (tACL_CONN *)NULL) + return(BTM_UNKNOWN_ADDR); + + /* If the connection is not in sniff mode, cannot cancel */ + if (p->mode != BTM_ACL_MODE_SNIFF) + return(BTM_WRONG_MODE); + + if (!btsnd_hcic_exit_sniff_mode (p->hci_handle)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); +} + + +/******************************************************************************* +** +** Function BTM_SetParkMode +** +** Description This function is called to set a connection into park mode. +** A check is made if the connection is already in sniff or park +** mode, and if yes, the park mode is ignored. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetParkMode (BD_ADDR remote_bda, UINT16 beacon_min_period, UINT16 beacon_max_period) +{ + tACL_CONN *p; + + BTM_TRACE_DEBUG0 ("BTM_SetParkMode"); + /* First, check if park mode is supported */ + if (!HCI_PARK_MODE_SUPPORTED(BTM_ReadLocalFeatures())) + return(BTM_MODE_UNSUPPORTED); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + /* If the connection is in sniff mode, forget about parking it */ + if (p->mode != BTM_ACL_MODE_NORMAL) + return(BTM_WRONG_MODE); + + /* no park mode if SCO exists -- CR#1982, 1.1 errata 1124 + command status event should be returned /w error code 0x0C "Command Disallowed" + Let LM do this. + */ + if (!btsnd_hcic_park_mode (p->hci_handle, + beacon_max_period, beacon_min_period)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_CancelParkMode +** +** Description This function is called to put a connection out of park mode. +** A check is made if the connection is already in park mode, +** and if not, the cancel sniff mode is ignored. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelParkMode (BD_ADDR remote_bda) +{ + tACL_CONN *p; + + BTM_TRACE_DEBUG0 ("BTM_CancelParkMode"); + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + /* If the connection is not in park mode, cannot cancel */ + if (p->mode != BTM_ACL_MODE_PARK) + return(BTM_WRONG_MODE); + + if (!btsnd_hcic_exit_park_mode (p->hci_handle)) + return(BTM_NO_RESOURCES); + + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ + + +/******************************************************************************* +** +** Function BTM_SetPacketTypes +** +** Description This function is set the packet types used for a specific +** ACL connection, +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPacketTypes (BD_ADDR remote_bda, UINT16 pkt_types) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("BTM_SetPacketTypes"); + + if ((p = btm_bda_to_acl(remote_bda)) != NULL) + return(btm_set_packet_types (p, pkt_types)); + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function BTM_ReadPacketTypes +** +** Description This function is set the packet types used for a specific +** ACL connection, +** +** Returns packet types supported for the connection, or 0 if no BD address +** +*******************************************************************************/ +UINT16 BTM_ReadPacketTypes (BD_ADDR remote_bda) +{ + tACL_CONN *p; + + BTM_TRACE_DEBUG0 ("BTM_ReadPacketTypes"); + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + return(p->pkt_types_mask); + } + + /* If here, no BD Addr found */ + return(0); +} + + +/******************************************************************************* +** +** Function BTM_ReadAclMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +#if BTM_PWR_MGR_INCLUDED == FALSE +tBTM_STATUS BTM_ReadAclMode (BD_ADDR remote_bda, UINT8 *p_mode) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadAclMode: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + *p_mode = p->mode; + return(BTM_SUCCESS); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ + +/******************************************************************************* +** +** Function BTM_ReadClockOffset +** +** Description This returns the clock offset for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Returns clock-offset or 0 if unknown +** +*******************************************************************************/ +UINT16 BTM_ReadClockOffset (BD_ADDR remote_bda) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadClockOffset: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + if ( (p = btm_bda_to_acl(remote_bda)) != NULL) + return(p->clock_offset); + + /* If here, no BD Addr found */ + return(0); +} + +/******************************************************************************* +** +** Function BTM_IsAclConnectionUp +** +** Description This function is called to check if an ACL connection exists +** to a specific remote BD Address. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_IsAclConnectionUp (BD_ADDR remote_bda) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadClockOffset: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + return(TRUE); + } + + /* If here, no BD Addr found */ + return(FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetNumAclLinks +** +** Description This function is called to count the number of +** ACL links that are active. +** +** Returns UINT16 Number of active ACL links +** +*******************************************************************************/ +UINT16 BTM_GetNumAclLinks (void) +{ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + return(UINT16)btm_cb.num_acl; +#else + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT16 xx, yy; + BTM_TRACE_DEBUG0 ("BTM_GetNumAclLinks"); + for (xx = yy = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if (p->in_use) + yy++; + } + + return(yy); +#endif +} + +/******************************************************************************* +** +** Function btm_get_acl_disc_reason_code +** +** Description This function is called to get the disconnection reason code +** returned by the HCI at disconnection complete event. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ +UINT16 btm_get_acl_disc_reason_code (void) +{ + UINT8 res = btm_cb.acl_disc_reason; + BTM_TRACE_DEBUG0 ("btm_get_acl_disc_reason_code"); + return(res); +} + + +/******************************************************************************* +** +** Function BTM_GetHCIConnHandle +** +** Description This function is called to get the handle for an ACL connection +** to a specific remote BD Address. +** +** Returns the handle of the connection, or 0xFFFF if none. +** +*******************************************************************************/ +UINT16 BTM_GetHCIConnHandle (BD_ADDR remote_bda) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("BTM_GetHCIConnHandle"); + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + return(p->hci_handle); + } + + /* If here, no BD Addr found */ + return(0xFFFF); +} + +#if BTM_PWR_MGR_INCLUDED == FALSE +/******************************************************************************* +** +** Function btm_process_mode_change +** +** Description This function is called when an HCI mode change event occurs. +** +** Input Parms hci_status - status of the event (HCI_SUCCESS if no errors) +** hci_handle - connection handle associated with the change +** mode - HCI_MODE_ACTIVE, HCI_MODE_HOLD, HCI_MODE_SNIFF, or HCI_MODE_PARK +** interval - number of baseband slots (meaning depends on mode) +** +** Returns void +** +*******************************************************************************/ +void btm_process_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, UINT16 interval) +{ + tACL_CONN *p; + UINT8 xx; + BTM_TRACE_DEBUG0 ("btm_process_mode_change"); + if (hci_status != HCI_SUCCESS) + { + BTM_TRACE_WARNING1 ("BTM: HCI Mode Change Error Status: 0x%02x", hci_status); + } + + /* Look up the connection by handle and set the current mode */ + xx = btm_handle_to_acl_index(hci_handle); + + /* don't assume that we can never get a bad hci_handle */ + if (xx >= MAX_L2CAP_LINKS) + return; + + p = &btm_cb.acl_db[xx]; + + /* If status is not success mode does not mean anything */ + if (hci_status == HCI_SUCCESS) + p->mode = mode; + + /* If mode change was because of an active role switch or change link key */ + btm_cont_rswitch_or_chglinkkey(p, btm_find_dev(p->remote_addr), hci_status); +} +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ + +/******************************************************************************* +** +** Function btm_process_clk_off_comp_evt +** +** Description This function is called when clock offset command completes. +** +** Input Parms hci_handle - connection handle associated with the change +** clock offset +** +** Returns void +** +*******************************************************************************/ +void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset) +{ + UINT8 xx; + BTM_TRACE_DEBUG0 ("btm_process_clk_off_comp_evt"); + /* Look up the connection by handle and set the current mode */ + if ((xx = btm_handle_to_acl_index(hci_handle)) < MAX_L2CAP_LINKS) + btm_cb.acl_db[xx].clock_offset = clock_offset; +} + +/******************************************************************************* +** +** Function btm_acl_role_changed +** +** Description This function is called whan a link's master/slave role change +** event or command status event (with error) is received. +** It updates the link control block, and calls +** the registered callback with status and role (if registered). +** +** Returns void +** +*******************************************************************************/ +void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role) +{ + UINT8 *p_bda = (bd_addr) ? bd_addr : btm_cb.devcb.switch_role_ref_data.remote_bd_addr; + tACL_CONN *p = btm_bda_to_acl(p_bda); + tBTM_ROLE_SWITCH_CMPL *p_data = &btm_cb.devcb.switch_role_ref_data; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_ROLE_CHG_DATA evt; +#endif +#if BTM_DISC_DURING_RS == TRUE + tBTM_SEC_DEV_REC *p_dev_rec; +#endif + BTM_TRACE_DEBUG0 ("btm_acl_role_changed"); + /* Ignore any stray events */ + if (p == NULL) + { + /* it could be a failure */ + if (hci_status != HCI_SUCCESS) + btm_acl_report_role_change(hci_status, bd_addr); + return; + } + + p_data->hci_status = hci_status; + + if (hci_status == HCI_SUCCESS) + { + p_data->role = new_role; + memcpy(p_data->remote_bd_addr, p_bda, BD_ADDR_LEN); + + /* Update cached value */ + p->link_role = new_role; + + /* Reload LSTO: link supervision timeout is reset in the LM after a role switch */ + if (new_role == BTM_ROLE_MASTER) + { + BTM_SetLinkSuperTout (p->remote_addr, p->link_super_tout); + } + } + else + { + /* so the BTM_BL_ROLE_CHG_EVT uses the old role */ + new_role = p->link_role; + } + + /* Check if any SCO req is pending for role change */ + btm_sco_chk_pend_rolechange (p->hci_handle); + + /* if switching state is switching we need to turn encryption on */ + /* if idle, we did not change encryption */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING) + { + /* Make sure there's not also a change link key going on before re-enabling */ + if (p->change_key_state != BTM_ACL_SWKEY_STATE_SWITCHING) + { + if (btsnd_hcic_set_conn_encrypt (p->hci_handle, TRUE)) + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON; + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON; + return; + } + } + else /* Set the state and wait for change link key */ + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON; + return; + } + } + + /* Set the switch_role_state to IDLE since the reply received from HCI */ + /* regardless of its result either success or failed. */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE; + } + + /* if role switch complete is needed, report it now */ + btm_acl_report_role_change(hci_status, bd_addr); + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + /* if role change event is registered, report it now */ + if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK)) + { + evt.event = BTM_BL_ROLE_CHG_EVT; + evt.new_role = new_role; + evt.p_bda = p_bda; + evt.hci_status = hci_status; + (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt); + } + + BTM_TRACE_DEBUG3("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d", + p_data->role, p_data->hci_status, p->switch_role_state); +#endif + +#if BTM_DISC_DURING_RS == TRUE + /* If a disconnect is pending, issue it now that role switch has completed */ + if ((p_dev_rec = btm_find_dev (p_bda)) != NULL) + { + if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) + { + BTM_TRACE_WARNING0("btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_ERROR2("tBTM_SEC_DEV:0x%x rs_disc_pending=%d", (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } + +#endif + +} + +#if (RFCOMM_INCLUDED==TRUE) +/******************************************************************************* +** +** Function BTM_AllocateSCN +** +** Description Look through the Server Channel Numbers for a free one. +** +** Returns Allocated SCN number or 0 if none. +** +*******************************************************************************/ + +UINT8 BTM_AllocateSCN(void) +{ + UINT8 x; + BTM_TRACE_DEBUG0 ("BTM_AllocateSCN"); + + // stack reserves scn 1 for HFP, HSP we still do the correct way + for (x = 1; x < BTM_MAX_SCN; x++) + { + if (!btm_cb.btm_scn[x]) + { + btm_cb.btm_scn[x] = TRUE; + return(x+1); + } + } + + return(0); /* No free ports */ +} + +/******************************************************************************* +** +** Function BTM_TryAllocateSCN +** +** Description Try to allocate a fixed server channel +** +** Returns Returns TRUE if server channel was available +** +*******************************************************************************/ + +BOOLEAN BTM_TryAllocateSCN(UINT8 scn) +{ + UINT8 x; + + /* Make sure we don't exceed max port range. + * Stack reserves scn 1 for HFP, HSP we still do the correct way. + */ + if ( (scn>=BTM_MAX_SCN) || (scn == 1) ) + return FALSE; + + /* check if this port is available */ + if (!btm_cb.btm_scn[scn-1]) + { + btm_cb.btm_scn[scn-1] = TRUE; + return TRUE; + } + + return (FALSE); /* Port was busy */ +} + +/******************************************************************************* +** +** Function BTM_FreeSCN +** +** Description Free the specified SCN. +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN BTM_FreeSCN(UINT8 scn) +{ + BTM_TRACE_DEBUG0 ("BTM_FreeSCN "); + if (scn <= BTM_MAX_SCN) + { + btm_cb.btm_scn[scn-1] = FALSE; + return(TRUE); + } + else + return(FALSE); /* Illegal SCN passed in */ +} + +#else + +/* Make dummy functions for the RPC to link against */ +UINT8 BTM_AllocateSCN(void) +{ + return(0); +} + +BOOLEAN BTM_FreeSCN(UINT8 scn) +{ + return(FALSE); +} + +#endif + + +/******************************************************************************* +** +** Function btm_acl_timeout +** +** Description This function is called when a timer list entry expires. +** +** Returns void +** +*******************************************************************************/ +void btm_acl_timeout (TIMER_LIST_ENT *p_tle) +{ + UINT32 timer_type = p_tle->param; + + BTM_TRACE_DEBUG0 ("btm_acl_timeout"); + if (timer_type == TT_DEV_RLNKP) + { + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb; + tBTM_LNK_POLICY_RESULTS lnkpol; + + lnkpol.status = BTM_ERR_PROCESSING; + lnkpol.settings = 0; + + btm_cb.devcb.p_rlinkp_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)(&lnkpol); + } +} + +/******************************************************************************* +** +** Function btm_set_packet_types +** +** Description This function sets the packet types used for a specific +** ACL connection. It is called internally by btm_acl_created +** or by an application/profile by BTM_SetPacketTypes. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types) +{ + UINT16 temp_pkt_types; + BTM_TRACE_DEBUG0 ("btm_set_packet_types"); + /* Save in the ACL control blocks, types that we support */ + temp_pkt_types = (pkt_types & BTM_ACL_SUPPORTED_PKTS_MASK & + btm_cb.btm_acl_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((pkt_types & BTM_ACL_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_acl_pkt_types_supported & BTM_ACL_EXCEPTION_PKTS_MASK)); + } + else + { + temp_pkt_types &= (~BTM_ACL_EXCEPTION_PKTS_MASK); + } + + /* Exclude packet types not supported by the peer */ + btm_acl_chk_peer_pkt_type_support (p, &temp_pkt_types); + + BTM_TRACE_DEBUG1 ("SetPacketType Mask -> 0x%04x", temp_pkt_types); + + if (!btsnd_hcic_change_conn_type (p->hci_handle, temp_pkt_types)) + { + return(BTM_NO_RESOURCES); + } + + p->pkt_types_mask = temp_pkt_types; + + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_get_max_packet_size +** +** Returns Returns maximum packet size that can be used for current +** connection, 0 if connection is not established +** +*******************************************************************************/ +UINT16 btm_get_max_packet_size (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr); + UINT16 pkt_types = 0; + UINT16 pkt_size = 0; + BTM_TRACE_DEBUG0 ("btm_get_max_packet_size"); + if (p != NULL) + { + pkt_types = p->pkt_types_mask; + } + else + { + /* Special case for when info for the local device is requested */ + if (memcmp (btm_cb.devcb.local_addr, addr, BD_ADDR_LEN) == 0) + { + pkt_types = btm_cb.btm_acl_pkt_types_supported; + } + } + + if (pkt_types) + { + if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH5)) + pkt_size = HCI_EDR3_DH5_PACKET_SIZE; + else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH5)) + pkt_size = HCI_EDR2_DH5_PACKET_SIZE; + else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH3)) + pkt_size = HCI_EDR3_DH3_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH5) + pkt_size = HCI_DH5_PACKET_SIZE; + else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH3)) + pkt_size = HCI_EDR2_DH3_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM5) + pkt_size = HCI_DM5_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH3) + pkt_size = HCI_DH3_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM3) + pkt_size = HCI_DM3_PACKET_SIZE; + else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH1)) + pkt_size = HCI_EDR3_DH1_PACKET_SIZE; + else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH1)) + pkt_size = HCI_EDR2_DH1_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH1) + pkt_size = HCI_DH1_PACKET_SIZE; + else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM1) + pkt_size = HCI_DM1_PACKET_SIZE; + } + +#ifdef BRCM_VS + /* Using HCI size 1017 instead of 1021 */ + if ((pkt_size == HCI_EDR3_DH5_PACKET_SIZE) + && (btu_cb.hcit_acl_data_size == 1017)) + pkt_size = 1017; +#endif + + return(pkt_size); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteVersion +** +** Returns If connected report peer device info +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRemoteVersion (BD_ADDR addr, UINT8 *lmp_version, + UINT16 *manufacturer, UINT16 *lmp_sub_version) +{ + tACL_CONN *p = btm_bda_to_acl(addr); + BTM_TRACE_DEBUG0 ("BTM_ReadRemoteVersion"); + if (p == NULL) + return(BTM_UNKNOWN_ADDR); + + if (lmp_version) + *lmp_version = p->lmp_version; + + if (manufacturer) + *manufacturer = p->manufacturer; + + if (lmp_sub_version) + *lmp_sub_version = p->lmp_subversion; + + return(BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteFeatures +** +** Returns pointer to the features string +** +*******************************************************************************/ +UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr); + BTM_TRACE_DEBUG0 ("BTM_ReadRemoteFeatures"); + if (p == NULL) + { + return(NULL); + } + + return(p->features); +} + +/******************************************************************************* +** +** Function BTM_RegBusyLevelNotif +** +** Description This function is called to register a callback to receive +** busy level change events. +** +** Returns BTM_SUCCESS if successfully registered, otherwise error +** +*******************************************************************************/ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) +tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level, + tBTM_BL_EVENT_MASK evt_mask) +{ + BTM_TRACE_DEBUG0 ("BTM_RegBusyLevelNotif"); + if (p_level) + *p_level = btm_cb.busy_level; + + btm_cb.bl_evt_mask = evt_mask; + + if (!p_cb) + btm_cb.p_bl_changed_cb = NULL; + else if (btm_cb.p_bl_changed_cb) + return(BTM_BUSY); + else + btm_cb.p_bl_changed_cb = p_cb; + + return(BTM_SUCCESS); +} +#else +/******************************************************************************* +** +** Function BTM_AclRegisterForChanges +** +** Returns This function is called to register a callback for when the +** ACL database changes, i.e. new entry or entry deleted. +** +*******************************************************************************/ +tBTM_STATUS BTM_AclRegisterForChanges (tBTM_ACL_DB_CHANGE_CB *p_cb) +{ + BTM_TRACE_DEBUG0 ("BTM_AclRegisterForChanges"); + if (!p_cb) + btm_cb.p_acl_changed_cb = NULL; + else if (btm_cb.p_acl_changed_cb) + return(BTM_BUSY); + else + btm_cb.p_acl_changed_cb = p_cb; + + return(BTM_SUCCESS); +} +#endif + +/******************************************************************************* +** +** Function BTM_SetQoS +** +** Description This function is called to setup QoS +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetQoS (BD_ADDR bd, FLOW_SPEC *p_flow, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + + BTM_TRACE_API6 ("BTM_SetQoS: BdAddr: %02x%02x%02x%02x%02x%02x", + bd[0], bd[1], bd[2], + bd[3], bd[4], bd[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_qossu_cmpl_cb) + return(BTM_BUSY); + + if ( (p = btm_bda_to_acl(bd)) != NULL) + { + btu_start_timer (&btm_cb.devcb.qossu_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + btm_cb.devcb.p_qossu_cmpl_cb = p_cb; + + if (!btsnd_hcic_qos_setup (p->hci_handle, p_flow->qos_flags, p_flow->service_type, + p_flow->token_rate, p_flow->peak_bandwidth, p_flow->latency,p_flow->delay_variation)) + { + btm_cb.devcb.p_qossu_cmpl_cb = NULL; + btu_stop_timer(&btm_cb.devcb.qossu_timer); + return(BTM_NO_RESOURCES); + } + else + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function btm_qos_setup_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the qos setup request. +** +** Returns void +** +*******************************************************************************/ +void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_qossu_cmpl_cb; + tBTM_QOS_SETUP_CMPL qossu; + BTM_TRACE_DEBUG0 ("btm_qos_setup_complete"); + btu_stop_timer (&btm_cb.devcb.qossu_timer); + + btm_cb.devcb.p_qossu_cmpl_cb = NULL; + + if (p_cb) + { + memset(&qossu, 0, sizeof(tBTM_QOS_SETUP_CMPL)); + qossu.status = status; + qossu.handle = handle; + if (p_flow != NULL) + { + qossu.flow.qos_flags = p_flow->qos_flags; + qossu.flow.service_type = p_flow->service_type; + qossu.flow.token_rate = p_flow->token_rate; + qossu.flow.peak_bandwidth = p_flow->peak_bandwidth; + qossu.flow.latency = p_flow->latency; + qossu.flow.delay_variation = p_flow->delay_variation; + } + BTM_TRACE_DEBUG1 ("BTM: p_flow->delay_variation: 0x%02x", + qossu.flow.delay_variation); + (*p_cb)(&qossu); + } +} + + +/******************************************************************************* +** +** Function BTM_ReadRSSI +** +** Description This function is called to read the link policy settings. +** The address of link policy results are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRSSI (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadRSSI: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_rssi_cmpl_cb) + return(BTM_BUSY); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + btu_start_timer (&btm_cb.devcb.rssi_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_rssi_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_rssi (p->hci_handle)) + { + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.rssi_timer); + return(BTM_NO_RESOURCES); + } + else + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_ReadLinkQuality +** +** Description This function is called to read the link qulaity. +** The value of the link quality is returned in the callback. +** (tBTM_LINK_QUALITY_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLinkQuality (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API6 ("BTM_ReadLinkQuality: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_lnk_qual_cmpl_cb) + return(BTM_BUSY); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + btu_start_timer (&btm_cb.devcb.lnk_quality_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + btm_cb.devcb.p_lnk_qual_cmpl_cb = p_cb; + + if (!btsnd_hcic_get_link_quality (p->hci_handle)) + { + btu_stop_timer (&btm_cb.devcb.lnk_quality_timer); + btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL; + return(BTM_NO_RESOURCES); + } + else + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return(BTM_UNKNOWN_ADDR); +} + +/******************************************************************************* +** +** Function BTM_ReadTxPower +** +** Description This function is called to read the current +** TX power of the connection. The tx power level results +** are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated or error code +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + BOOLEAN ret; +#define BTM_READ_RSSI_TYPE_CUR 0x00 +#define BTM_READ_RSSI_TYPE_MAX 0X01 + + BTM_TRACE_API6 ("BTM_ReadTxPower: RemBdAddr: %02x%02x%02x%02x%02x%02x", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_tx_power_cmpl_cb) + return(BTM_BUSY); + + p = btm_bda_to_acl(remote_bda); + if (p != (tACL_CONN *)NULL) + { + btu_start_timer (&btm_cb.devcb.tx_power_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_tx_power_cmpl_cb = p_cb; + +#if BLE_INCLUDED == TRUE + if (p->is_le_link) + { + memcpy(btm_cb.devcb.read_tx_pwr_addr, remote_bda, BD_ADDR_LEN); + ret = btsnd_hcic_ble_read_adv_chnl_tx_power(); + } + else +#endif + { + ret = btsnd_hcic_read_tx_power (p->hci_handle, BTM_READ_RSSI_TYPE_CUR); + } + if (!ret) + { + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + return(BTM_NO_RESOURCES); + } + else + return(BTM_CMD_STARTED); + } + + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} +/******************************************************************************* +** +** Function btm_read_tx_power_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read tx power request. +** +** Returns void +** +*******************************************************************************/ +void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_tx_power_cmpl_cb; + tBTM_TX_POWER_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT16 index; + BTM_TRACE_DEBUG0 ("btm_read_tx_power_complete"); + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) + { + results.status = BTM_SUCCESS; + + if (!is_ble) + { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (results.tx_power, p); + + /* Search through the list of active channels for the correct BD Addr */ + for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) + { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + break; + } + } + } +#if BLE_INCLUDED == TRUE + else + { + STREAM_TO_UINT8 (results.tx_power, p); + memcpy(results.rem_bda, btm_cb.devcb.read_tx_pwr_addr, BD_ADDR_LEN); + } +#endif + BTM_TRACE_DEBUG2 ("BTM TX power Complete: tx_power %d, hci status 0x%02x", + results.tx_power, results.hci_status); + } + else + results.status = BTM_ERR_PROCESSING; + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_read_rssi_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read rssi request. +** +** Returns void +** +*******************************************************************************/ +void btm_read_rssi_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + tBTM_RSSI_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT16 index; + BTM_TRACE_DEBUG0 ("btm_read_rssi_complete"); + btu_stop_timer (&btm_cb.devcb.rssi_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) + { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (results.rssi, p); + BTM_TRACE_DEBUG2 ("BTM RSSI Complete: rssi %d, hci status 0x%02x", + results.rssi, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) + { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + break; + } + } + } + else + results.status = BTM_ERR_PROCESSING; + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_read_link_quality_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read link quality. +** +** Returns void +** +*******************************************************************************/ +void btm_read_link_quality_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_lnk_qual_cmpl_cb; + tBTM_LINK_QUALITY_RESULTS results; + UINT16 handle; + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT16 index; + BTM_TRACE_DEBUG0 ("btm_read_link_quality_complete"); + btu_stop_timer (&btm_cb.devcb.rssi_timer); + + /* If there was a callback registered for read rssi, call it */ + btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) + { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (results.link_quality, p); + BTM_TRACE_DEBUG2 ("BTM Link Quality Complete: Link Quality %d, hci status 0x%02x", + results.link_quality, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle)) + { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + break; + } + } + } + else + results.status = BTM_ERR_PROCESSING; + + (*p_cb)(&results); + } +} + +/******************************************************************************* +** +** Function btm_remove_acl +** +** Description This function is called to disconnect an ACL connection +** +** Returns BTM_SUCCESS if successfully initiated, otherwise BTM_NO_RESOURCES. +** +*******************************************************************************/ +tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr) +{ + UINT16 hci_handle = BTM_GetHCIConnHandle(bd_addr); + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_DEBUG0 ("btm_remove_acl"); +#if BTM_DISC_DURING_RS == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + /* Role Switch is pending, postpone until completed */ + if (p_dev_rec && (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING)) + { + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + } + else /* otherwise can disconnect right away */ +#endif + + if (hci_handle != 0xFFFF) + { + if (!btsnd_hcic_disconnect (hci_handle, HCI_ERR_PEER_USER)) + status = BTM_NO_RESOURCES; + } + else + status = BTM_UNKNOWN_ADDR; + + return status; +} + + +/******************************************************************************* +** +** Function BTM_SetTraceLevel +** +** Description This function sets the trace level for BTM. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 BTM_SetTraceLevel (UINT8 new_level) +{ + BTM_TRACE_DEBUG0 ("BTM_SetTraceLevel"); + if (new_level != 0xFF) + btm_cb.trace_level = new_level; + + return(btm_cb.trace_level); +} + +/******************************************************************************* +** +** Function btm_cont_rswitch_or_chglinkkey +** +** Description This function is called to continue processing an active +** role switch or change of link key procedure. It first +** disables encryption if enabled and EPR is not supported +** +** Returns void +** +*******************************************************************************/ +void btm_cont_rswitch_or_chglinkkey (tACL_CONN *p, tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status) +{ + BOOLEAN sw_ok = TRUE; + BOOLEAN chlk_ok = TRUE; + BTM_TRACE_DEBUG0 ("btm_cont_rswitch_or_chglinkkey "); + /* Check to see if encryption needs to be turned off if pending + change of link key or role switch */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE || + p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + { + /* Must turn off Encryption first if necessary */ + /* Some devices do not support switch or change of link key while encryption is on */ + if (p_dev_rec != NULL && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) + && !BTM_EPR_AVAILABLE(p)) + { + if (btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE)) + { + p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF; + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + + if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF; + } + else + { + /* Error occurred; set states back to Idle */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + sw_ok = FALSE; + + if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + chlk_ok = FALSE; + } + } + else /* Encryption not used or EPR supported, continue with switch + and/or change of link key */ + { + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; +#if BTM_DISC_DURING_RS == TRUE + if (p_dev_rec) + p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING; +#endif + sw_ok = btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role); + } + + if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS; + chlk_ok = btsnd_hcic_change_link_key (p->hci_handle); + } + } + + if (!sw_ok) + { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + btm_acl_report_role_change(hci_status, p->remote_addr); + } + + if (!chlk_ok) + { + p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE; + if (btm_cb.devcb.p_chg_link_key_cb) + { + btm_cb.devcb.chg_link_key_ref_data.hci_status = hci_status; + (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data); + btm_cb.devcb.p_chg_link_key_cb = NULL; + } + } + } +} + +/******************************************************************************* +** +** Function btm_acl_resubmit_page +** +** Description send pending page request +** +*******************************************************************************/ +void btm_acl_resubmit_page (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BT_HDR *p_buf; + UINT8 *pp; + BD_ADDR bda; + BTM_TRACE_DEBUG0 ("btm_acl_resubmit_page"); + /* If there were other page request schedule can start the next one */ + if ((p_buf = (BT_HDR *)GKI_dequeue (&btm_cb.page_queue)) != NULL) + { + /* skip 3 (2 bytes opcode and 1 byte len) to get to the bd_addr + * for both create_conn and rmt_name */ + pp = (UINT8 *)(p_buf + 1) + p_buf->offset + 3; + + STREAM_TO_BDADDR (bda, pp); + + p_dev_rec = btm_find_or_alloc_dev (bda); + + memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p_buf); + } + else + btm_cb.paging = FALSE; +} + +/******************************************************************************* +** +** Function btm_acl_reset_paging +** +** Description set paging to FALSE and free the page queue - called at hci_reset +** +*******************************************************************************/ +void btm_acl_reset_paging (void) +{ + BT_HDR *p; + BTM_TRACE_DEBUG0 ("btm_acl_reset_paging"); + /* If we sent reset we are definitely not paging any more */ + while ((p = (BT_HDR *)GKI_dequeue(&btm_cb.page_queue)) != NULL) + GKI_freebuf (p); + + btm_cb.paging = FALSE; +} + +/******************************************************************************* +** +** Function btm_acl_set_discing +** +** Description set discing to the given value +** +*******************************************************************************/ +void btm_acl_set_discing (BOOLEAN discing) +{ + BTM_TRACE_DEBUG0 ("btm_acl_set_discing"); + btm_cb.discing = discing; +} + +/******************************************************************************* +** +** Function btm_acl_paging +** +** Description send a paging command or queue it in btm_cb +** +*******************************************************************************/ +void btm_acl_paging (BT_HDR *p, BD_ADDR bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG4 ("btm_acl_paging discing:%d, paging:%d BDA: %06x%06x", + btm_cb.discing, btm_cb.paging, + (bda[0]<<16) + (bda[1]<<8) + bda[2], (bda[3]<<16) + (bda[4] << 8) + bda[5]); + if (btm_cb.discing) + { + btm_cb.paging = TRUE; + GKI_enqueue (&btm_cb.page_queue, p); + } + else + { + if (!BTM_ACL_IS_CONNECTED (bda)) + { + BTM_TRACE_DEBUG2 ("connecting_bda: %06x%06x", + (btm_cb.connecting_bda[0]<<16) + (btm_cb.connecting_bda[1]<<8) + btm_cb.connecting_bda[2], + (btm_cb.connecting_bda[3]<<16) + (btm_cb.connecting_bda[4] << 8) + btm_cb.connecting_bda[5]); + if (btm_cb.paging && + memcmp (bda, btm_cb.connecting_bda, BD_ADDR_LEN) != 0) + { + GKI_enqueue (&btm_cb.page_queue, p); + } + else + { + p_dev_rec = btm_find_or_alloc_dev (bda); + memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + } + + btm_cb.paging = TRUE; + } + else /* ACL is already up */ + { + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_notif_conn_collision +** +** Description Send connection collision event to upper layer if registered +** +** Returns TRUE if sent out to upper layer, +** FALSE if BTM_BUSY_LEVEL_CHANGE_INCLUDED == FALSE, or no one +** needs the notification. +** +** Note: Function only used if BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE +** +*******************************************************************************/ +BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda) +{ +#if (BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_EVENT_DATA evt_data; + + /* Report possible collision to the upper layer. */ + if (btm_cb.p_bl_changed_cb) + { + BTM_TRACE_DEBUG6 ("btm_acl_notif_conn_collision: RemBdAddr: %02x%02x%02x%02x%02x%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + evt_data.event = BTM_BL_COLLISION_EVT; + evt_data.conn.p_bda = bda; + (*btm_cb.p_bl_changed_cb)(&evt_data); + return TRUE; + } + else + return FALSE; +#else + return FALSE; +#endif +} + + +/******************************************************************************* +** +** Function btm_acl_chk_peer_pkt_type_support +** +** Description Check if peer supports requested packets +** +*******************************************************************************/ +void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type) +{ + /* 3 and 5 slot packets? */ + if (!HCI_3_SLOT_PACKETS_SUPPORTED(p->features)) + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH3 +BTM_ACL_PKT_TYPES_MASK_DM3); + + if (!HCI_5_SLOT_PACKETS_SUPPORTED(p->features)) + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5); + + /* If HCI version > 2.0, then also check EDR packet types */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + /* 2 and 3 MPS support? */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p->features)) + /* Not supported. Add 'not_supported' mask for all 2MPS packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH5); + + if (!HCI_EDR_ACL_3MPS_SUPPORTED(p->features)) + /* Not supported. Add 'not_supported' mask for all 3MPS packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + + /* EDR 3 and 5 slot support? */ + if (HCI_EDR_ACL_2MPS_SUPPORTED(p->features) || HCI_EDR_ACL_3MPS_SUPPORTED(p->features)) + { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p->features)) + /* Not supported. Add 'not_supported' mask for all 3-slot EDR packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3); + + if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p->features)) + /* Not supported. Add 'not_supported' mask for all 5-slot EDR packet types */ + *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + } +} diff --git a/stack/btm/btm_ble.c b/stack/btm/btm_ble.c new file mode 100644 index 0000000..74b5767 --- /dev/null +++ b/stack/btm/btm_ble.c @@ -0,0 +1,1900 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE device control utilities, and LE + * security functions. + * + ******************************************************************************/ + +#include + +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "btm_ble_api.h" +#include "smp_api.h" + +#if SMP_INCLUDED == TRUE +extern BOOLEAN AES_CMAC ( BT_OCTET16 key, UINT8 *input, UINT16 length, UINT16 tlen, UINT8 *p_signature); +extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable); +extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda); +#endif + +static void btm_ble_update_active_bgconn_scan_params(void); + +/*******************************************************************************/ +/* External Function to be called by other modules */ +/*******************************************************************************/ +/******************************************************** +** +** Function BTM_SecAddBleDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** bd_name - Name of the peer device. NULL if unknown. +** dev_type - Remote device's device type. +** addr_type - LE device address type. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, tBT_DEVICE_TYPE dev_type, + tBLE_ADDR_TYPE addr_type) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 i = 0; + tBTM_INQ_INFO *p_info=NULL; + + BTM_TRACE_DEBUG1 ("BTM_SecAddBleDevice dev_type=0x%x", dev_type); + p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) + { + BTM_TRACE_DEBUG0("Add a new device"); + + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + BTM_TRACE_DEBUG1 ("allocate a new dev rec idx=0x%x ", i ); + p_dev_rec = &btm_cb.sec_dev_rec[i]; + + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + + /* update conn params, use default value for background connection params */ + p_dev_rec->conn_params.min_conn_int = + p_dev_rec->conn_params.max_conn_int = + p_dev_rec->conn_params.supervision_tout = + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_PARAM_UNDEF; + + BTM_TRACE_DEBUG1 ("hci_handl=0x%x ", p_dev_rec->hci_handle ); + break; + } + } + + if (!p_dev_rec) + return(FALSE); + } + else + { + BTM_TRACE_DEBUG0("Device already exist"); + } + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + p_dev_rec->device_type = dev_type; + p_dev_rec->ble.ble_addr_type = addr_type; + BTM_TRACE_DEBUG3 ("p_dev_rec->device_type =0x%x addr_type=0x%x sec_flags=0x%x", + dev_type, addr_type, p_dev_rec->sec_flags); + + /* sync up with the Inq Data base*/ + p_info = BTM_InqDbRead(bd_addr); + if (p_info) + { + p_info->results.ble_addr_type = p_dev_rec->ble.ble_addr_type ; + p_info->results.device_type = p_dev_rec->device_type; + BTM_TRACE_DEBUG2 ("InqDb device_type =0x%x addr_type=0x%x", + p_info->results.device_type, p_info->results.ble_addr_type); + } + +#endif + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_SecAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +* +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, tBTM_LE_KEY_TYPE key_type) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_DEBUG0 ("BTM_SecAddBleKey"); + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || !p_le_key || + (key_type != BTM_LE_KEY_PENC && key_type != BTM_LE_KEY_PID && + key_type != BTM_LE_KEY_PCSRK && key_type != BTM_LE_KEY_LENC)) + { + BTM_TRACE_WARNING3 ("BTM_SecAddLeKey() No BT Link Key, Wrong Type, or No Device record \ + for bdaddr: %08x%04x, Type: %d", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], key_type); + return(FALSE); + } + + BTM_TRACE_DEBUG3 ("BTM_SecAddLeKey() BDA: %08x%04x, Type: 0x%02x", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], key_type); + + if (key_type == BTM_LE_KEY_PENC || key_type == BTM_LE_KEY_PID || + key_type == BTM_LE_KEY_PCSRK || key_type == BTM_LE_KEY_LENC || + key_type == BTM_LE_KEY_LCSRK) + { + btm_sec_save_le_key (bd_addr, key_type, p_le_key, FALSE); + } + +#endif + + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_BleLoadLocalKeys +** +** Description Local local identity key, encryption root or sign counter. +** +** Parameters: key_type: type of key, can be BTM_BLE_KEY_TYPE_ID, BTM_BLE_KEY_TYPE_ER +** or BTM_BLE_KEY_TYPE_COUNTER. +** p_key: pointer to the key. +* +** Returns non2. +** +*******************************************************************************/ +void BTM_BleLoadLocalKeys(UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key) +{ +#if BLE_INCLUDED == TRUE + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + BTM_TRACE_DEBUG0 ("BTM_BleLoadLocalKeys"); + if (p_key != NULL) + { + switch (key_type) + { + case BTM_BLE_KEY_TYPE_ID: + memcpy(&p_devcb->id_keys, &p_key->id_keys, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + break; + + case BTM_BLE_KEY_TYPE_ER: + memcpy(p_devcb->er, p_key->er, sizeof(BT_OCTET16)); + break; + + default: + BTM_TRACE_ERROR1("unknow local key type: %d", key_type); + break; + } + } +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +void BTM_GetDeviceEncRoot (BT_OCTET16 er) +{ + BTM_TRACE_DEBUG0 ("BTM_GetDeviceEncRoot"); + +#if BLE_INCLUDED == TRUE + memcpy (er, btm_cb.devcb.er, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceIDRoot +** +** Description This function is called to read the local device identity +** root. +** +** Returns void +** the local device IR is copied into irk +** +*******************************************************************************/ +void BTM_GetDeviceIDRoot (BT_OCTET16 irk) +{ + BTM_TRACE_DEBUG0 ("BTM_GetDeviceIDRoot "); + +#if BLE_INCLUDED == TRUE + memcpy (irk, btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceDHK +** +** Description This function is called to read the local device DHK. +** +** Returns void +** the local device DHK is copied into dhk +** +*******************************************************************************/ +void BTM_GetDeviceDHK (BT_OCTET16 dhk) +{ +#if BLE_INCLUDED == TRUE + BTM_TRACE_DEBUG0 ("BTM_GetDeviceDHK"); + memcpy (dhk, btm_cb.devcb.id_keys.dhk, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to set the local device random address +** . +** +** Returns void +** +*******************************************************************************/ +void BTM_ReadConnectionAddr (BD_ADDR conn_addr) +{ +#if BLE_INCLUDED == TRUE + BTM_TRACE_DEBUG0 ("BTM_ReadConnectionAddr"); + if (btm_cb.ble_ctr_cb.inq_var.own_addr_type == BLE_ADDR_PUBLIC) + { + BTM_GetLocalDeviceAddr(conn_addr); + } + else + { + memcpy (conn_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } +#endif +} + +/******************************************************************************* +** +** Function BTM_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation BTM_SUCCESS if success. +** Otherwise, BTM_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +void BTM_SecurityGrant(BD_ADDR bd_addr, UINT8 res) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS; + BTM_TRACE_DEBUG0 ("BTM_SecurityGrant"); + SMP_SecurityGrant(bd_addr, res_smp); +#endif +} + +/******************************************************************************* +** +** Function BTM_BlePasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation BTM_SUCCESS if success +** key_len - length in bytes of the Passkey +** p_passkey - pointer to array with the passkey +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +void BTM_BlePasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; + + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + BTM_TRACE_DEBUG0 ("BTM_BlePasskeyReply"); + SMP_PasskeyReply(bd_addr, res_smp, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_OOB_FAIL; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG0 ("BTM_BleOobDataReply"); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + SMP_OobDataReply(bd_addr, res_smp, len, p_data); +#endif +} + +/****************************************************************************** +** +** Function BTM_BleSetConnScanParams +** +** Description Set scan parameter used in BLE connection request +** +** Parameters: scan_interval: scan interval +** scan_window: scan window +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetConnScanParams (UINT16 scan_interval, UINT16 scan_window) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + BOOLEAN new_param = FALSE; + + if (BTM_BLE_VALID_PRAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_VALID_PRAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) + { + btu_stop_timer(&p_ble_cb->scan_param_idle_timer); + + if (p_ble_cb->scan_int != scan_interval) + { + p_ble_cb->scan_int = scan_interval; + new_param = TRUE; + } + + if (p_ble_cb->scan_win != scan_window) + { + p_ble_cb->scan_win = scan_window; + new_param = TRUE; + } + + if (new_param) + btm_ble_update_active_bgconn_scan_params(); + } + else + { + BTM_TRACE_ERROR0("Illegal Connection Scan Parameters"); + } +#endif +} + +/******************************************************** +** +** Function BTM_BleSetPrefConnParams +** +** Description Set a peripheral's preferred connection parameters +** +** Parameters: bd_addr - BD address of the peripheral +** scan_interval: scan interval +** scan_window: scan window +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetPrefConnParams (BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_API4 ("BTM_BleSetPrefConnParams min: %u max: %u latency: %u \ + tout: %u", + min_conn_int, max_conn_int, slave_latency, supervision_tout); + + if (BTM_BLE_VALID_PRAM(min_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_VALID_PRAM(max_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_VALID_PRAM(supervision_tout, BTM_BLE_CONN_SUP_TOUT_MIN, BTM_BLE_CONN_SUP_TOUT_MAX) && + slave_latency <= BTM_BLE_CONN_LATENCY_MAX) + { + if (p_dev_rec) + { + /* expect conn int and stout and slave latency to be updated all together */ + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF || max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + { + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.min_conn_int = min_conn_int; + else + p_dev_rec->conn_params.min_conn_int = max_conn_int; + + if (max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.max_conn_int = max_conn_int; + else + p_dev_rec->conn_params.max_conn_int = min_conn_int; + + if (slave_latency != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.slave_latency = slave_latency; + else + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_SLAVE_LATENCY_DEF; + + if (supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.supervision_tout = supervision_tout; + else + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_TIMEOUT_DEF; + + } + + } + else + { + BTM_TRACE_ERROR0("Unknown Device, setting rejected"); + } + } + else + { + BTM_TRACE_ERROR0("Illegal Connection Parameters"); + } +#endif /* BLE_INCLUDED */ +} + +/******************************************************************************* +** +** Function BTM_ReadDevInfo +** +** Description This function is called to read the device/address type +** of BD address. +** +** Parameter remote_bda: remote device address +** p_dev_type: output parameter to read the device type. +** p_addr_type: output parameter to read the address type. +** +*******************************************************************************/ +void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, tBLE_ADDR_TYPE *p_addr_type) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (remote_bda); + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(remote_bda); + + *p_dev_type = BT_DEVICE_TYPE_BREDR; + *p_addr_type = BLE_ADDR_PUBLIC; + + if (!p_dev_rec) + { + /* Check with the BT manager if details about remote device are known */ + if (p_inq_info != NULL) + { + *p_dev_type = p_inq_info->results.device_type ; + *p_addr_type = p_inq_info->results.ble_addr_type; + } + /* unknown device, assume BR/EDR */ + } + else /* there is a security device record exisitng */ + { + /* new inquiry result, overwrite device type in security device record */ + if (p_inq_info) + { + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + } + *p_dev_type = p_dev_rec->device_type; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + + } + + BTM_TRACE_DEBUG2 ("btm_find_dev_type - device_type = %d addr_type = %d", *p_dev_type , *p_addr_type); +#endif + + return; +} + + +/******************************************************************************* +** Internal Functions +*******************************************************************************/ +#if BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_update_active_bgconn_scan_params +** +** Description This function is called to update the scan parameter if background +** connection has been active. +** +*******************************************************************************/ +static void btm_ble_update_active_bgconn_scan_params(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_BLE_SEL_CBACK *p_select_cback; + + /* if active , cancel and restart and apply the params */ + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + if (btm_ble_start_auto_conn(FALSE)) + btm_ble_start_auto_conn(TRUE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = p_cb->p_select_cback; + if (btm_ble_start_select_conn(FALSE, NULL)) + btm_ble_start_select_conn(TRUE, p_select_cback); + } + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_check_link_type +** +** Description This function is to check the link type is BLE or BR/EDR. +** +** Returns TRUE if BLE link; FALSE if BR/EDR. +** +*******************************************************************************/ +BOOLEAN btm_ble_check_link_type (BD_ADDR bd_addr) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("btm_ble_check_link_type"); + if ((p = btm_bda_to_acl(bd_addr)) != NULL) + return p->is_le_link; + else + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_rand_enc_complete +** +** Description This function is the callback functions for HCI_Rand command +** and HCI_Encrypt command is completed. +** This message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback) +{ + tBTM_RAND_ENC params; + UINT8 *p_dest = params.param_buf; + + BTM_TRACE_DEBUG0 ("btm_ble_rand_enc_complete"); + + memset(¶ms, 0, sizeof(tBTM_RAND_ENC)); + + /* If there was a callback address for vcs complete, call it */ + if (p_enc_cplt_cback && p) + { + /* Pass paramters to the callback function */ + STREAM_TO_UINT8(params.status, p); /* command status */ + + if (params.status == HCI_SUCCESS) + { + params.opcode = op_code; + + if (op_code == HCI_BLE_RAND) + params.param_len = BT_OCTET8_LEN; + else + params.param_len = BT_OCTET16_LEN; + + memcpy(p_dest, p, params.param_len); /* Fetch return info from HCI event message */ + } + if (p_enc_cplt_cback) + (*p_enc_cplt_cback)(¶ms); /* Call the Encryption complete callback function */ + } +} + + +#if (SMP_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to increment local sign counter +** Returns None +** +*******************************************************************************/ +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG1 ("btm_ble_increment_sign_ctr is_local=%d", is_local); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + if (is_local) + p_dev_rec->ble.keys.local_counter++; + else + p_dev_rec->ble.keys.counter++; + BTM_TRACE_DEBUG3 ("is_local=%d local sign counter=%d peer sign counter=%d", + is_local, + p_dev_rec->ble.keys.local_counter, + p_dev_rec->ble.keys.counter); + } +} + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to get the BLE key type that has been exchanged +** in betweem local device and peer device. +** +** Returns p_key_type: output parameter to carry the key type value. +** +*******************************************************************************/ +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG0 ("btm_ble_get_enc_key_type"); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + *p_key_types = p_dev_rec->ble.key_type; + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_get_local_div +** +** Description This function is called to read the local DIV +** +** Returns TURE - if a valid DIV is availavle +*******************************************************************************/ +BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN status = FALSE; + BTM_TRACE_DEBUG0 ("btm_get_local_div"); + + BTM_TRACE_DEBUG6("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0],bd_addr[1], + bd_addr[2],bd_addr[3], + bd_addr[4],bd_addr[5]); + + p_dev_rec = btm_find_dev (bd_addr); + + if (p_dev_rec && p_dev_rec->ble.keys.div) + { + status = TRUE; + *p_div = p_dev_rec->ble.keys.div; + } + BTM_TRACE_DEBUG2 ("btm_get_local_div status=%d (1-OK) DIV=0x%x", status, *p_div); + return status; +} + +/******************************************************************************* +** +** Function btm_sec_save_le_key +** +** Description This function is called by the SMP to update +** an BLE key. SMP is internal, whereas all the keys shall +** be sent to the application. The function is also called +** when application passes ble key stored in NVRAM to the btm_sec. +** pass_to_application parameter is false in this case. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, + BOOLEAN pass_to_application) +{ + tBTM_SEC_DEV_REC *p_rec; + tBTM_LE_EVT_DATA cb_data; + + BTM_TRACE_DEBUG2 ("btm_sec_save_le_key key_type=0x%x pass_to_application=%d",key_type, pass_to_application); + /* Store the updated key in the device database */ + + BTM_TRACE_DEBUG6("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0],bd_addr[1], + bd_addr[2],bd_addr[3], + bd_addr[4],bd_addr[5]); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL && p_keys) + { + switch (key_type) + { + case BTM_LE_KEY_PENC: + memcpy(p_rec->ble.keys.ltk, p_keys->penc_key.ltk, BT_OCTET16_LEN); + memcpy(p_rec->ble.keys.rand, p_keys->penc_key.rand, BT_OCTET8_LEN); + p_rec->ble.keys.sec_level = p_keys->penc_key.sec_level; + p_rec->ble.keys.ediv = p_keys->penc_key.ediv; + p_rec->ble.keys.key_size = p_keys->penc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_PENC; + p_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + if (p_keys->penc_key.sec_level == SMP_SEC_AUTHENTICATED) + p_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + else + p_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + BTM_TRACE_DEBUG3("BTM_LE_KEY_PENC key_type=0x%x sec_flags=0x%x sec_leve=0x%x", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.sec_level); + break; + + case BTM_LE_KEY_PID: + memcpy(p_rec->ble.keys.irk, p_keys->pid_key, BT_OCTET16_LEN); + p_rec->ble.key_type |= BTM_LE_KEY_PID; + BTM_TRACE_DEBUG1("BTM_LE_KEY_PID key_type=0x%x save peer IRK", p_rec->ble.key_type); + break; + + case BTM_LE_KEY_PCSRK: + memcpy(p_rec->ble.keys.csrk, p_keys->pcsrk_key.csrk, BT_OCTET16_LEN); + p_rec->ble.keys.srk_sec_level = p_keys->pcsrk_key.sec_level; + p_rec->ble.keys.counter = p_keys->pcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_PCSRK; + p_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + if ( p_keys->pcsrk_key.sec_level== SMP_SEC_AUTHENTICATED) + p_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + else + p_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + + BTM_TRACE_DEBUG4("BTM_LE_KEY_PCSRK key_type=0x%x sec_flags=0x%x sec_level=0x%x peer_counter=%d", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.srk_sec_level, + p_rec->ble.keys.counter ); + break; + + case BTM_LE_KEY_LENC: + p_rec->ble.keys.div = p_keys->lenc_key.div; /* update DIV */ + p_rec->ble.keys.sec_level = p_keys->lenc_key.sec_level; + p_rec->ble.keys.key_size = p_keys->lenc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_LENC; + + BTM_TRACE_DEBUG4("BTM_LE_KEY_LENC key_type=0x%x DIV=0x%x key_size=0x%x sec_level=0x%x", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.key_size, + p_rec->ble.keys.sec_level ); + break; + + case BTM_LE_KEY_LCSRK:/* local CSRK has been delivered */ + p_rec->ble.keys.div = p_keys->lcsrk_key.div; /* update DIV */ + p_rec->ble.keys.local_csrk_sec_level = p_keys->lcsrk_key.sec_level; + p_rec->ble.keys.local_counter = p_keys->lcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_LCSRK; + BTM_TRACE_DEBUG4("BTM_LE_KEY_LCSRK key_type=0x%x DIV=0x%x scrk_sec_level=0x%x local_counter=%d", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.local_csrk_sec_level, + p_rec->ble.keys.local_counter ); + break; + + default: + BTM_TRACE_WARNING1("btm_sec_save_le_key (Bad key_type 0x%02x)", key_type); + return; + } + + BTM_TRACE_DEBUG3 ("BLE key type 0x%02x updated for BDA: %08x%04x (btm_sec_save_le_key)", key_type, + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5]); + + /* Notify the application that one of the BLE keys has been updated + If link key is in progress, it will get sent later.*/ + if (pass_to_application && btm_cb.api.p_le_callback) + { + cb_data.key.p_key_value = p_keys; + cb_data.key.key_type = key_type; + + (*btm_cb.api.p_le_callback) (BTM_LE_KEY_EVT, bd_addr, &cb_data); + } + return; + } + + BTM_TRACE_WARNING3 ("BLE key type 0x%02x called for Unknown BDA or type: %08x%04x !! (btm_sec_save_le_key)", key_type, + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5]); + + if (p_rec) + { + BTM_TRACE_DEBUG1 ("sec_flags=0x%x", p_rec->sec_flags); + } +} + +/******************************************************************************* +** +** Function btm_ble_update_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size) +{ + tBTM_SEC_DEV_REC *p_rec; + + BTM_TRACE_DEBUG1("btm_ble_update_sec_key_size enc_key_size = %d", enc_key_size); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) + { + p_rec->enc_key_size = enc_key_size; + } +} + +/******************************************************************************* +** +** Function btm_ble_read_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_rec; + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) + { + return p_rec->enc_key_size; + } + else + return 0; +} + +/******************************************************************************* +** +** Function btm_ble_link_sec_check +** +** Description Check BLE link security level match. +** +** Returns TRUE: check is OK and the *p_sec_req_act contain the action +** +*******************************************************************************/ +void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 req_sec_level, cur_sec_level; + + BTM_TRACE_DEBUG1 ("btm_ble_link_sec_check auth_req =0x%x", auth_req); + + if (p_dev_rec == NULL) + { + BTM_TRACE_ERROR0 ("btm_ble_link_sec_check received for unknown device"); + return; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + { + /* race condition: discard the security request while master is encrypting the link */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_DISCARD; + } + else + { + req_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + if ((auth_req == (BTM_LE_AUTH_REQ_BOND|BTM_LE_AUTH_REQ_MITM)) || + (auth_req == (BTM_LE_AUTH_REQ_MITM)) ) + { + req_sec_level = BTM_LE_SEC_AUTHENTICATED; + } + + BTM_TRACE_DEBUG1 ("dev_rec sec_flags=0x%x", p_dev_rec->sec_flags); + + /* currently encrpted */ + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + { + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED) + cur_sec_level = BTM_LE_SEC_AUTHENTICATED; + else + cur_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + } + else /* unencrypted link */ + { + /* if bonded, get the key security level */ + if (p_dev_rec->ble.key_type & BTM_LE_KEY_PENC) + cur_sec_level = p_dev_rec->ble.keys.sec_level; + else + cur_sec_level = BTM_LE_SEC_NONE; + } + + if (cur_sec_level >= req_sec_level) + { + if (cur_sec_level == BTM_LE_SEC_NONE) + { + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_NONE; + } + else + { + /* To avoid re-encryption on an encrypted link for an equal condition encryption + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_DISCARD; + else + */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_ENCRYPT; + } + } + else + { + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_PAIR; /* start the pariring process to upgrade the keys*/ + } + } + + BTM_TRACE_DEBUG3("cur_sec_level=%d req_sec_level=%d sec_req_act=%d", + cur_sec_level, + req_sec_level, + *p_sec_req_act); + +} + +/******************************************************************************* +** +** Function btm_ble_set_encryption +** +** Description This function is called to ensure that LE connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role) +{ + tBTM_STATUS cmd = BTM_NO_RESOURCES; + tBTM_BLE_SEC_ACT sec_act = *(tBTM_BLE_SEC_ACT *)p_ref_data ; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG2 ("btm_ble_set_encryption sec_act=0x%x role_master=%d", sec_act, p_rec->role_master); + + if (p_rec == NULL) + { + return(BTM_WRONG_MODE); + } + + if (sec_act == BTM_BLE_SEC_ENCRYPT_MITM) + { + p_rec->security_required |= BTM_SEC_IN_MITM; + } + + switch (sec_act) + { + case BTM_BLE_SEC_ENCRYPT: + if (link_role == BTM_ROLE_MASTER) + { + /* start link layer encryption using the security info stored */ + btm_ble_start_encrypt(bd_addr, FALSE, NULL); + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + cmd = BTM_CMD_STARTED; + break; + } + /* if salve role then fall through to call SMP_Pair below which will send a + sec_request to request the master to encrypt the link */ + case BTM_BLE_SEC_ENCRYPT_NO_MITM: + case BTM_BLE_SEC_ENCRYPT_MITM: + + if (SMP_Pair(bd_addr) == SMP_STARTED) + { + cmd = BTM_CMD_STARTED; + p_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + } + break; + + default: + cmd = BTM_SUCCESS; + break; + } + return cmd; +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request +** +** Description This function is called when encryption request is received +** on a slave device. +** +** +** Returns void +** +*******************************************************************************/ +void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BT_OCTET8 dummy_stk = {0}; + + BTM_TRACE_DEBUG0 ("btm_ble_ltk_request"); + + p_cb->ediv = ediv; + + memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN); + + if (!smp_proc_ltk_request(p_dev_rec->bd_addr)) + btm_ble_ltk_request_reply(p_dev_rec->bd_addr, FALSE, dummy_stk); + + +} + +/******************************************************************************* +** +** Function btm_ble_start_encrypt +** +** Description This function is called to start LE encryption. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + BT_OCTET8 dummy_rand = {0}; + + BTM_TRACE_DEBUG0 ("btm_ble_start_encrypt"); + + if (!p_rec || + (p_rec && p_rec->sec_state == BTM_SEC_STATE_ENCRYPTING)) + return FALSE; + + if (p_rec->sec_state == BTM_SEC_STATE_IDLE) + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + p_cb->enc_handle = p_rec->hci_handle; + + if (use_stk) + { + if (!btsnd_hcic_ble_start_enc(p_rec->hci_handle, dummy_rand, 0, stk)) + return FALSE; + } + else + { + if (!btsnd_hcic_ble_start_enc(p_rec->hci_handle, p_rec->ble.keys.rand, + p_rec->ble.keys.ediv, p_rec->ble.keys.ltk)) + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_link_encrypted +** +** Description This function is called when LE link encrption status is changed. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG1 ("btm_ble_link_encrypted encr_enable=%d", encr_enable); + + smp_link_encrypted(bd_addr, encr_enable); + + if (p_dev_rec) + { + BTM_TRACE_DEBUG1(" p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags); + + if (encr_enable && p_dev_rec->enc_key_size == 0) + p_dev_rec->enc_key_size = p_dev_rec->ble.keys.key_size; + + if (p_dev_rec->p_callback) + { + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + { + if (encr_enable) + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_SUCCESS); + else if (p_dev_rec->role_master) + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING); + } + } + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } +} + +/******************************************************************************* +** Function btm_enc_proc_ltk +** Description send LTK reply when it's ready. +*******************************************************************************/ +static void btm_enc_proc_ltk(tSMP_ENC *p) +{ + UINT8 i; + BTM_TRACE_DEBUG0 ("btm_enc_proc_ltk"); + if (p && p->param_len == BT_OCTET16_LEN) + { + for (i = 0; i < (BT_OCTET16_LEN - btm_cb.key_size); i ++) + p->param_buf[BT_OCTET16_LEN - i - 1] = 0; + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p->param_buf); + } +} + +/******************************************************************************* +** Function btm_enc_proc_slave_y +** Description calculate LTK when Y is ready +*******************************************************************************/ +static void btm_enc_proc_slave_y(tSMP_ENC *p) +{ + UINT16 div, y; + UINT8 *pp = p->param_buf; + tBTM_CB *p_cb = &btm_cb; + tSMP_ENC output; + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG0 ("btm_enc_proc_slave_y"); + if (p != NULL) + { + STREAM_TO_UINT16(y, pp); + + div = p_cb->ediv ^ y; + p_dev_rec = btm_find_dev_by_handle (p_cb->enc_handle); + + if ( p_dev_rec && + p_dev_rec->ble.keys.div == div ) + { + BTM_TRACE_DEBUG0 ("LTK request OK"); + /* calculating LTK , LTK = E er(div) */ + SMP_Encrypt(p_cb->devcb.er, BT_OCTET16_LEN, (UINT8 *)&div, 2, &output); + btm_enc_proc_ltk(&output); + } + else + { + BTM_TRACE_DEBUG0 ("LTK request failed - send negative reply"); + btsnd_hcic_ble_ltk_req_neg_reply(p_cb->enc_handle); + if (p_dev_rec) + btm_ble_link_encrypted(p_dev_rec->bd_addr, 0); + + } + } +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request_reply +** +** Description This function is called to send a LTK request reply on a slave +** device. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + tBTM_CB *p_cb = &btm_cb; + tSMP_ENC output; + + if (p_rec == NULL) + { + BTM_TRACE_ERROR0("btm_ble_ltk_request_reply received for unknown device"); + return; + } + + BTM_TRACE_DEBUG0 ("btm_ble_ltk_request_reply"); + p_cb->enc_handle = p_rec->hci_handle; + p_cb->key_size = p_rec->ble.keys.key_size; + + BTM_TRACE_ERROR1("key size = %d", p_rec->ble.keys.key_size); + if (use_stk) + { + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, stk); + } + else /* calculate LTK using peer device */ + { + /* generate Y= Encrypt(DHK, Rand) received from encrypt request */ + SMP_Encrypt(p_cb->devcb.id_keys.dhk, BT_OCTET16_LEN, p_cb->enc_rand, + BT_OCTET8_LEN, &output); + btm_enc_proc_slave_y(&output); + } +} + +/******************************************************************************* +** +** Function btm_ble_io_capabilities_req +** +** Description This function is called to handle SMP get IO capability request. +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_io_capabilities_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data) +{ + UINT8 callback_rc = BTM_SUCCESS; + BTM_TRACE_DEBUG0 ("btm_ble_io_capabilities_req"); + if (btm_cb.api.p_le_callback) + { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != p_data->oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; + + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 1: p_dev_rec->security_required = %d auth_req:%d", + p_dev_rec->security_required, p_data->auth_req); + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 2: i_keys=0x%x r_keys=0x%x (bit 0-LTK 1-IRK 2-CSRK)", + p_data->init_keys, + p_data->resp_keys); + + /* if authentication requires MITM protection, put on the mask */ + if (p_dev_rec->security_required & BTM_SEC_IN_MITM) + p_data->auth_req |= BTM_LE_AUTH_REQ_MITM; + + if (!(p_data->auth_req & SMP_AUTH_BOND)) + { + BTM_TRACE_DEBUG0("Non bonding: No keys should be exchanged"); + p_data->init_keys = 0; + p_data->resp_keys = 0; + } + + BTM_TRACE_DEBUG1 ("btm_ble_io_capabilities_req 3: auth_req:%d", p_data->auth_req); + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 4: i_keys=0x%x r_keys=0x%x", + p_data->init_keys, + p_data->resp_keys); + + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 5: p_data->io_cap = %d auth_req:%d", + p_data->io_cap, p_data->auth_req); + + /* remove MITM protection requirement if IO cap does not allow it */ + if ((p_data->io_cap == BTM_IO_CAP_NONE) && p_data->oob_data == SMP_OOB_NONE) + p_data->auth_req &= ~BTM_LE_AUTH_REQ_MITM; + + BTM_TRACE_DEBUG3 ("btm_ble_io_capabilities_req 6: IO_CAP:%d oob_data:%d auth_req:%d", + p_data->io_cap, p_data->oob_data, p_data->auth_req); + } + return callback_rc; +} + +/***************************************************************************** +** Function btm_proc_smp_cback +** +** Description This function is the SMP callback handler. +** +******************************************************************************/ +UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 res; + + BTM_TRACE_DEBUG1 ("btm_proc_smp_cback event = %d", event); + + if (p_dev_rec != NULL) + { + switch (event) + { + case SMP_IO_CAP_REQ_EVT: + btm_ble_io_capabilities_req(p_dev_rec, (tBTM_LE_IO_REQ *)&p_data->io_req); + break; + + case SMP_PASSKEY_REQ_EVT: + case SMP_PASSKEY_NOTIF_EVT: + case SMP_OOB_REQ_EVT: + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + case SMP_SEC_REQUEST_EVT: + case SMP_COMPLT_EVT: + if (btm_cb.api.p_le_callback) + { + /* the callback function implementation may change the IO capability... */ + BTM_TRACE_DEBUG1 ("btm_cb.api.p_le_callback=0x%x", btm_cb.api.p_le_callback ); + (*btm_cb.api.p_le_callback) (event, bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } + else + { + BTM_TRACE_ERROR0 ("btm_proc_smp_cback: btm_cb.api.p_le_callback ==NULL"); + } + + if (event == SMP_COMPLT_EVT) + { + BTM_TRACE_DEBUG2 ("evt=SMP_COMPLT_EVT before update sec_level=0x%x sec_flags=0x%x", p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + res = (p_data->cmplt.reason == SMP_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING; + + BTM_TRACE_DEBUG3 ("after update result=%d sec_level=0x%x sec_flags=0x%x", + res, p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + btm_sec_dev_rec_cback_event(p_dev_rec, res); + + if (p_data->cmplt.is_pair_cancel && btm_cb.api.p_bond_cancel_cmpl_callback ) + { + BTM_TRACE_DEBUG0 ("Pairing Cancel completed"); + (*btm_cb.api.p_bond_cancel_cmpl_callback)(BTM_SUCCESS); + } +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (res != BTM_SUCCESS) + { + if (!btm_cb.devcb.no_disc_if_pair_fail) + { + BTM_TRACE_DEBUG0 ("Pairing failed - Remove ACL"); + btm_remove_acl(bd_addr); + } + else + { + BTM_TRACE_DEBUG0 ("Pairing failed - Not Removing ACL"); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#else + if (res != BTM_SUCCESS) + btm_remove_acl(bd_addr); +#endif + + BTM_TRACE_DEBUG3 ("btm_cb pairing_state=%x pairing_flags=%x pin_code_len=%x", + btm_cb.pairing_state, + btm_cb.pairing_flags, + btm_cb.pin_code_len ); + BTM_TRACE_DEBUG6 ("btm_cb.pairing_bda %02x:%02x:%02x:%02x:%02x:%02x", + btm_cb.pairing_bda[0], btm_cb.pairing_bda[1], btm_cb.pairing_bda[2], + btm_cb.pairing_bda[3], btm_cb.pairing_bda[4], btm_cb.pairing_bda[5]); + + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.pairing_flags = 0; + } + break; + + default: + BTM_TRACE_DEBUG1 ("unknown event = %d", event); + break; + + + } + } + else + { + BTM_TRACE_ERROR0("btm_proc_smp_cback received for unknown device"); + } + + return 0; +} + +#endif /* SMP_INCLUDED */ +#endif /* BLE_INCLUDED */ + + +/******************************************************************************* +** +** Function BTM_BleDataSignature +** +** Description This function is called to sign the data using AES128 CMAC +** algorith. +** +** Parameter bd_addr: target device the data to be signed for. +** p_text: singing data +** len: length of the data to be signed. +** signature: output parameter where data signature is going to +** be stored. +** +** Returns TRUE if signing sucessul, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature) +{ + BOOLEAN ret = FALSE; +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + UINT8 *p_buf, *pp; + + BT_OCTET16 er; + UINT16 div; + UINT8 temp[4]; /* for (r || DIV) r=1*/ + UINT16 r=1; + UINT8 *p=temp, *p_mac = (UINT8 *)signature; + tSMP_ENC output; + BT_OCTET16 local_csrk; + + BTM_TRACE_DEBUG0 ("BTM_BleDataSignature"); + if (p_rec == NULL) + { + BTM_TRACE_ERROR0("data signing can not be done from unknow device"); + } + else + { + if ((p_buf = (UINT8 *)GKI_getbuf((UINT16)(len + 4))) != NULL) + { + BTM_TRACE_DEBUG0("Start to generate Local CSRK"); + /* prepare plain text */ + if (p_text) + { + memcpy(p_buf, p_text, len); + pp = (p_buf + len); + } + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if ( btm_cb.devcb.enable_test_local_sign_cntr) + { + BTM_TRACE_DEBUG1 ("Use Test local counter value from script counter_val=%d", btm_cb.devcb.test_local_sign_cntr); + UINT32_TO_STREAM(pp, btm_cb.devcb.test_local_sign_cntr); + } + else + { + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); + } +#else + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); +#endif + /* compute local csrk */ + if (btm_get_local_div(bd_addr, &div)) + { + BTM_TRACE_DEBUG1 ("compute_csrk div=%x", div); + BTM_GetDeviceEncRoot(er); + + /* CSRK = d1(ER, DIV, 1) */ + UINT16_TO_STREAM(p, div); + UINT16_TO_STREAM(p, r); + + if (!SMP_Encrypt(er, BT_OCTET16_LEN, temp, 4, &output)) + { + BTM_TRACE_ERROR0("Local CSRK generation failed "); + } + else + { + BTM_TRACE_DEBUG0("local CSRK generation success"); + memcpy((void *)local_csrk, output.param_buf, BT_OCTET16_LEN); + + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (btm_cb.devcb.enable_test_local_sign_cntr) + { + UINT32_TO_STREAM(p_mac, btm_cb.devcb.test_local_sign_cntr); + } + else + { + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); + } +#else + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); +#endif + + if ((ret = AES_CMAC(local_csrk, p_buf, (UINT16)(len + 4), BTM_CMAC_TLEN_SIZE, p_mac)) == TRUE) + { + btm_ble_increment_sign_ctr(bd_addr, TRUE); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if ( btm_cb.devcb.enable_test_mac_val) + { + BTM_TRACE_DEBUG0 ("Use MAC value from script"); + memcpy(p_mac, btm_cb.devcb.test_mac, BTM_CMAC_TLEN_SIZE); + } +#endif + } + BTM_TRACE_DEBUG1("BTM_BleDataSignature p_mac = %d", p_mac); + BTM_TRACE_DEBUG4("p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = 0x%02x", + *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); + BTM_TRACE_DEBUG4("p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = 0x%02x", + *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); + + GKI_freebuf(p_buf); + } + } + } + } +#endif /* BLE_INCLUDED */ + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleVerifySignature +** +** Description This function is called to verify the data signature +** +** Parameter bd_addr: target device the data to be signed for. +** p_orig: original data before signature. +** len: length of the signing data +** counter: counter used when doing data signing +** p_comp: signature to be compared against. + +** Returns TRUE if signature verified correctly; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BleVerifySignature (BD_ADDR bd_addr, UINT8 *p_orig, UINT16 len, UINT32 counter, + UINT8 *p_comp) +{ + BOOLEAN verified = FALSE; +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + UINT8 p_mac[BTM_CMAC_TLEN_SIZE]; + + if (p_rec == NULL || (p_rec && !(p_rec->ble.key_type & BTM_LE_KEY_PCSRK))) + { + BTM_TRACE_ERROR0("can not verify signature for unknown device"); + } + else if (counter < p_rec->ble.keys.counter) + { + BTM_TRACE_ERROR0("signature received with out dated sign counter"); + } + else if (p_orig == NULL) + { + BTM_TRACE_ERROR0("No signature to verify"); + } + else + { + BTM_TRACE_DEBUG2 ("BTM_BleVerifySignature rcv_cnt=%d >= expected_cnt=%d", counter, p_rec->ble.keys.counter); + + if (AES_CMAC(p_rec->ble.keys.csrk, p_orig, len, BTM_CMAC_TLEN_SIZE, p_mac)) + { + if (memcmp(p_mac, p_comp, BTM_CMAC_TLEN_SIZE) == 0) + { + btm_ble_increment_sign_ctr(bd_addr, FALSE); + verified = TRUE; + } + } + } +#endif /* BLE_INCLUDED */ + return verified; +} + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** Utility functions for LE device IR/ER generation +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_notify_new_key +** +** Description This function is to notify application new keys have been +** generated. +** +** Returns void +** +*******************************************************************************/ +static void btm_notify_new_key(UINT8 key_type) +{ + tBTM_BLE_LOCAL_KEYS *p_locak_keys = NULL; + + BTM_TRACE_DEBUG1 ("btm_notify_new_key key_type=%d", key_type); + + if (btm_cb.api.p_le_key_callback) + { + switch (key_type) + { + case BTM_BLE_KEY_TYPE_ID: + BTM_TRACE_DEBUG0 ("BTM_BLE_KEY_TYPE_ID"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.id_keys; + break; + + case BTM_BLE_KEY_TYPE_ER: + BTM_TRACE_DEBUG0 ("BTM_BLE_KEY_TYPE_ER"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.er; + break; + + default: + BTM_TRACE_ERROR1("unknown key type: %d", key_type); + break; + } + if (p_locak_keys != NULL) + (*btm_cb.api.p_le_key_callback) (key_type, p_locak_keys); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er2 +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er2(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_er2"); + + if (p &&p->opcode == HCI_BLE_RAND) + { + memcpy(&btm_cb.devcb.er[8], p->param_buf, BT_OCTET8_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ER); + } + else + { + BTM_TRACE_ERROR0("Generating ER2 exception."); + memset(&btm_cb.devcb.er, 0, sizeof(BT_OCTET16)); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_er"); + + if (p &&p->opcode == HCI_BLE_RAND) + { + memcpy(&btm_cb.devcb.er[0], p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er2)) + { + memset(&btm_cb.devcb.er, 0, sizeof(BT_OCTET16)); + BTM_TRACE_ERROR0("Generating ER2 failed."); + } + } + else + { + BTM_TRACE_ERROR0("Generating ER1 exception."); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_irk +** +** Description This function is called when IRK is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_irk(tSMP_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_irk"); + if (p &&p->opcode == HCI_BLE_ENCRYPT) + { + memcpy(btm_cb.devcb.id_keys.irk, p->param_buf, BT_OCTET16_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ID); + } + else + { + BTM_TRACE_ERROR0("Generating IRK exception."); + } + + /* proceed generate ER */ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er)) + { + BTM_TRACE_ERROR0("Generating ER failed."); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_dhk +** +** Description This function is called when DHK is calculated, store it in +** local control block, and proceed to generate ER, a 128-bits +** random number. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_dhk(tSMP_ENC *p) +{ +#if SMP_INCLUDED == TRUE + UINT8 btm_ble_irk_pt = 0x01; + tSMP_ENC output; + + BTM_TRACE_DEBUG0 ("btm_ble_process_dhk"); + + if (p && p->opcode == HCI_BLE_ENCRYPT) + { + memcpy(btm_cb.devcb.id_keys.dhk, p->param_buf, BT_OCTET16_LEN); + BTM_TRACE_DEBUG0("BLE DHK generated."); + + /* IRK = D1(IR, 1) */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_irk_pt, + 1, &output)) + { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } + else + { + btm_ble_process_irk(&output); + } + } + else + { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_process_ir2 +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x03, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir2(tBTM_RAND_ENC *p) +{ +#if SMP_INCLUDED == TRUE + UINT8 btm_ble_dhk_pt = 0x03; + tSMP_ENC output; + + BTM_TRACE_DEBUG0 ("btm_ble_process_ir2"); + + if (p && p->opcode == HCI_BLE_RAND) + { + /* remembering in control block */ + memcpy(&btm_cb.devcb.id_keys.ir[8], p->param_buf, BT_OCTET8_LEN); + /* generate DHK= Eir({0x03, 0x00, 0x00 ...}) */ + + + SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_dhk_pt, + 1, &output); + btm_ble_process_dhk(&output); + + BTM_TRACE_DEBUG0("BLE IR generated."); + } + else + { + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_process_ir +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x02, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_ir"); + + if (p && p->opcode == HCI_BLE_RAND) + { + /* remembering in control block */ + memcpy(btm_cb.devcb.id_keys.ir, p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir2)) + { + BTM_TRACE_ERROR0("Generating IR2 failed."); + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } + } +} + +/******************************************************************************* +** +** Function btm_ble_reset_id +** +** Description This function is called to reset LE device identity. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_reset_id( void ) +{ + BTM_TRACE_DEBUG0 ("btm_ble_reset_id"); + + /* regenrate Identity Root*/ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir)) + { + BTM_TRACE_DEBUG0("Generating IR failed."); + } +} + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function btm_ble_set_no_disc_if_pair_fail +** +** Description This function indicates that whether no disconnect of the ACL +** should be used if pairing failed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_no_disc_if_pair_fail(BOOLEAN disable_disc ) +{ + BTM_TRACE_DEBUG1 ("btm_ble_set_disc_enable_if_pair_fail disable_disc=%d", disable_disc); + btm_cb.devcb.no_disc_if_pair_fail = disable_disc; +} + +/******************************************************************************* +** +** Function btm_ble_set_test_mac_value +** +** Description This function set test MAC value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_mac_value(BOOLEAN enable, UINT8 *p_test_mac_val ) +{ + BTM_TRACE_DEBUG1 ("btm_ble_set_test_mac_value enable=%d", enable); + btm_cb.devcb.enable_test_mac_val = enable; + memcpy(btm_cb.devcb.test_mac, p_test_mac_val, BT_OCTET8_LEN); +} + +/******************************************************************************* +** +** Function btm_ble_set_test_local_sign_cntr_value +** +** Description This function set test local sign counter value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr ) +{ + BTM_TRACE_DEBUG2 ("btm_ble_set_test_local_sign_cntr_value enable=%d local_sign_cntr=%d", + enable, test_local_sign_cntr); + btm_cb.devcb.enable_test_local_sign_cntr = enable; + btm_cb.devcb.test_local_sign_cntr = test_local_sign_cntr; +} + +#endif /* BTM_BLE_CONFORMANCE_TESTING */ + + +#endif /* BLE_INCLUDED */ diff --git a/stack/btm/btm_ble_addr.c b/stack/btm/btm_ble_addr.c new file mode 100644 index 0000000..04872f9 --- /dev/null +++ b/stack/btm/btm_ble_addr.c @@ -0,0 +1,383 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE address management. + * + ******************************************************************************/ + +#include + +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" + + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + #include "smp_api.h" + #define BTM_BLE_PRIVATE_ADDR_INT 900 /* 15 minutes minimum for random address refreshing */ + +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_cmpl +** +** Description This is callback functioin when resolvable private address +** generation is complete. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_resolve_paddr_cmpl(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_gen_resolve_paddr_cmpl"); + if (p && p->param_buf) + { + /* get the high bytes of the random address */ + p_cb->private_addr[2] = p->param_buf[0]; + p_cb->private_addr[1] = p->param_buf[1]; + p_cb->private_addr[0] = p->param_buf[2]; + /* mask off the 1st MSB */ + p_cb->private_addr[0] &= 0xfe; + /* set the 2nd MSB to be 1 */ + p_cb->private_addr[0] |= 0x02; + /* set it to controller */ + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + p_inq_cb->own_addr_type = BLE_ADDR_RANDOM; + + /* start a periodical timer to refresh random addr */ + btu_stop_timer(&p_cb->raddr_timer_ent); + btu_start_timer (&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + + /* if adv is active, restart adv with new private addr */ + if (p_inq_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + + btsnd_hcic_ble_write_adv_params (p_inq_cb->adv_interval_min, + p_inq_cb->adv_interval_max, + p_inq_cb->evt_type, + p_inq_cb->own_addr_type, + p_inq_cb->direct_bda.type, + p_inq_cb->direct_bda.bda, + p_inq_cb->adv_chnl_map, + p_inq_cb->afp); + } + } + else + { + /* random address set failure */ + BTM_TRACE_DEBUG0("set random address failed"); + } +} +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_low +** +** Description This function is called when random address has generate the +** random number base for low 3 byte bd address. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_resolve_paddr_low(tBTM_RAND_ENC *p) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tSMP_ENC output; + + BTM_TRACE_EVENT0 ("btm_gen_resolve_paddr_low"); + if (p && p->param_buf) + { + p_cb->private_addr[5] = p->param_buf[0]; + p_cb->private_addr[4] = p->param_buf[1]; + p_cb->private_addr[3] = p->param_buf[2]; + + /* encrypt with ur IRK */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, p->param_buf, 3, &output)) + { + btm_gen_resolve_paddr_cmpl(NULL); + } + else + { + btm_gen_resolve_paddr_cmpl(&output); + } + } +#endif +} +/******************************************************************************* +** +** Function btm_gen_resolvable_private_addr +** +** Description This function generate a resolvable private address. +** +** Returns void +** +*******************************************************************************/ +void btm_gen_resolvable_private_addr (void) +{ + BTM_TRACE_EVENT0 ("btm_gen_resolvable_private_addr"); + /* generate 3B rand as BD LSB, SRK with it, get BD MSB */ + if (!btsnd_hcic_ble_rand((void *)btm_gen_resolve_paddr_low)) + btm_gen_resolve_paddr_cmpl(NULL); +} +/******************************************************************************* +** +** Function btm_gen_non_resolve_paddr_cmpl +** +** Description This is the callback function when non-resolvable private +** function is generated and write to controller. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_non_resolve_paddr_cmpl(tBTM_RAND_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 *pp; + BTM_TRACE_EVENT0 ("btm_gen_non_resolve_paddr_cmpl"); + if (p && p->param_buf) + { + pp = p->param_buf; + STREAM_TO_BDADDR(p_cb->private_addr, pp); + /* mask off the 2 MSB */ + p_cb->private_addr[0] &= 0xfc; + /* write to controller */ + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + btm_cb.ble_ctr_cb.inq_var.own_addr_type = BLE_ADDR_RANDOM; + } + else + { + BTM_TRACE_DEBUG0("btm_gen_non_resolvable_private_addr failed"); + } +} +/******************************************************************************* +** +** Function btm_gen_non_resolvable_private_addr +** +** Description This function generate a non-resolvable private address. +** +** +** Returns void +** +*******************************************************************************/ +void btm_gen_non_resolvable_private_addr (void) +{ + BTM_TRACE_EVENT0 ("btm_gen_non_resolvable_private_addr"); + if (!btsnd_hcic_ble_rand((void *)btm_gen_non_resolve_paddr_cmpl)) + { + btm_gen_non_resolve_paddr_cmpl(NULL); + } +} + #if SMP_INCLUDED == TRUE +/******************************************************************************* +** Utility functions for Random address resolving +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolve_address_cmpl +** +** Description This function sends the random address resolving complete +** callback. +** +** Returns None. +** +*******************************************************************************/ +static void btm_ble_resolve_address_cmpl(void) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_address_cmpl"); + if (p_mgnt_cb->index < BTM_SEC_MAX_DEVICE_RECORDS) + p_dev_rec = &btm_cb.sec_dev_rec[p_mgnt_cb->index]; + + p_mgnt_cb->busy = FALSE; + + (* p_mgnt_cb->p_resolve_cback)(p_dev_rec, p_mgnt_cb->p); +} +/******************************************************************************* +** +** Function btm_ble_proc_resolve_x +** +** Description This function compares the X with random address 3 MSO bytes +** to find a match, if not match, continue for next record. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_proc_resolve_x(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 comp[3]; + BTM_TRACE_EVENT0 ("btm_ble_proc_resolve_x"); + /* compare the hash with 3 LSB of bd address */ + comp[0] = p_mgnt_cb->random_bda[5]; + comp[1] = p_mgnt_cb->random_bda[4]; + comp[2] = p_mgnt_cb->random_bda[3]; + + if (p && p->param_buf) + { + if (!memcmp(p->param_buf, &comp[0], 3)) + { + /* match is found */ + BTM_TRACE_EVENT0 ("match is found"); + btm_ble_resolve_address_cmpl(); + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_ble_match_random_bda +** +** Description This function match the random address to the appointed device +** record, starting from calculating IRK. If record index exceed +** the maximum record number, matching failed and send callback. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_match_random_bda(UINT16 rec_index) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 rand[3]; + tSMP_ENC output; + + /* use the 3 MSB of bd address as prand */ + rand[0] = p_mgnt_cb->random_bda[2]; + rand[1] = p_mgnt_cb->random_bda[1]; + rand[2] = p_mgnt_cb->random_bda[0]; + + BTM_TRACE_EVENT1("btm_ble_match_random_bda rec_index = %d", rec_index); + + if (rec_index < BTM_SEC_MAX_DEVICE_RECORDS) + { + p_dev_rec = &btm_cb.sec_dev_rec[rec_index]; + + BTM_TRACE_ERROR2("sec_flags = %02x device_type = %d", p_dev_rec->sec_flags, p_dev_rec->device_type); + + if ((p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) && + (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) + { + /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */ + SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, + &rand[0], 3, &output); + return btm_ble_proc_resolve_x(&output); + } + else + { + // not completed + return FALSE; + } + } + else /* no match found */ + { + btm_ble_resolve_address_cmpl(); + return TRUE; + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr +** +** Description This function is called to resolve a random address. +** +** Returns pointer to the security record of the device whom a random +** address is matched to. +** +*******************************************************************************/ +void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK * p_cback, void *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_random_addr"); + if ( !p_mgnt_cb->busy) + { + p_mgnt_cb->p = p; + p_mgnt_cb->busy = TRUE; + p_mgnt_cb->index = 0; + p_mgnt_cb->p_resolve_cback = p_cback; + memcpy(p_mgnt_cb->random_bda, random_bda, BD_ADDR_LEN); + /* start to resolve random address */ + /* check for next security record */ + while (TRUE) + { + if (btm_ble_match_random_bda(p_mgnt_cb->index++)) + { + // match found or went through the list + break; + } + } + } + else + (*p_cback)(NULL, p); +} + #endif +/******************************************************************************* +** address mapping between pseudo address and real connection address +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_map_bda_to_conn_bda +** +** Description This function map a BD address to the real connection address +** and return the connection address type. +*******************************************************************************/ +tBLE_ADDR_TYPE btm_ble_map_bda_to_conn_bda(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + BTM_TRACE_EVENT0 ("btm_ble_map_bda_to_conn_bda"); + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL && + p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + if (p_dev_rec->ble.ble_addr_type != BLE_ADDR_PUBLIC) + { + memcpy(bd_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + } + return p_dev_rec->ble.ble_addr_type; + } + else + return BLE_ADDR_PUBLIC; +} +/******************************************************************************* +** +** Function btm_ble_map_bda_to_pseudo_bda +** +** Description This function map a BD address to a pseudo address when the +** address given is a random address. +** +*******************************************************************************/ +void btm_ble_map_bda_to_pseudo_bda(BD_ADDR bd_addr) +{ + BTM_TRACE_EVENT0 ("btm_ble_map_bda_to_pseudo_bda"); +} +#endif + + diff --git a/stack/btm/btm_ble_bgconn.c b/stack/btm/btm_ble_bgconn.c new file mode 100644 index 0000000..aa002e8 --- /dev/null +++ b/stack/btm/btm_ble_bgconn.c @@ -0,0 +1,616 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE whitelist operation. + * + ******************************************************************************/ + +#include + +#include "bt_types.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "hcimsgs.h" + +#ifndef BTM_BLE_SCAN_PARAM_TOUT +#define BTM_BLE_SCAN_PARAM_TOUT 50 /* 50 seconds */ +#endif + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_update_scanner_filter_policy +** +** Description This function update the filter policy of scnner or advertiser. +*******************************************************************************/ +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_update_scanner_filter_policy"); + btm_cb.ble_ctr_cb.inq_var.sfp = scan_policy; + + btsnd_hcic_ble_set_scan_params ((UINT8)((p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? BTM_BLE_SCAN_MODE_ACTI: p_inq->scan_type), + (UINT16)(!p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval), + (UINT16)(!p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window), + BLE_ADDR_PUBLIC, + scan_policy); +} +/******************************************************************************* +** +** Function btm_update_adv_filter_policy +** +** Description This function update the filter policy of scnner or advertiser. +*******************************************************************************/ +void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_update_adv_filter_policy"); + p_cb->afp = adv_policy; +} +/******************************************************************************* +** +** Function btm_update_dev_to_white_list +** +** Description This function adds a device into white list. +*******************************************************************************/ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type) +{ + /* look up the sec device record, and find the address */ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR dummy_bda = {0}; + BOOLEAN started = FALSE, suspend = FALSE; + + if (btm_cb.btm_inq_vars.inq_active) + { + suspend = TRUE; + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL && + p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + BTM_TRACE_DEBUG0("btm_update_dev_to_white_list 1"); + + if ((to_add && p_cb->num_empty_filter == 0) || + (!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries)) + { + BTM_TRACE_ERROR1("num_entry available in controller: %d", p_cb->num_empty_filter); + return started; + } + + + if ( p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_PUBLIC, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_PUBLIC, bd_addr); + } + else + { + if (BLE_ADDR_IS_STATIC(bd_addr)) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_RANDOM, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_RANDOM, bd_addr); + + } + if (memcmp(p_dev_rec->ble.reconn_addr, dummy_bda, BD_ADDR_LEN) != 0) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_RANDOM, p_dev_rec->ble.reconn_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_RANDOM, p_dev_rec->ble.reconn_addr); + } + } + } + /* if not a known device, shall we add it? */ + else + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (addr_type, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (addr_type, bd_addr); + } + + if (suspend) + { + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE); + } + + return started; +} +/******************************************************************************* +** +** Function btm_ble_clear_white_list +** +** Description This function clears the white list. +*******************************************************************************/ +void btm_ble_clear_white_list (void) +{ + BTM_TRACE_EVENT0 ("btm_ble_clear_white_list"); + btsnd_hcic_ble_clear_white_list(); +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list_complete +** +** Description This function clears the white list complete. +*******************************************************************************/ +void btm_ble_clear_white_list_complete(UINT8 *p_data, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 status; + BTM_TRACE_EVENT0 ("btm_ble_clear_white_list_complete"); + STREAM_TO_UINT8 (status, p_data); + + if (status == HCI_SUCCESS) + p_cb->num_empty_filter = p_cb->max_filter_entries; + +} +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_ble_add_2_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_add_2_white_list_complete"); + + if (*p == HCI_SUCCESS) + { + p_cb->num_empty_filter --; + } +} +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_remove_from_white_list_complete"); + if (*p == HCI_SUCCESS) + { + p_cb->num_empty_filter ++; + } +} +/******************************************************************************* +** +** Function btm_ble_find_dev_in_whitelist +** +** Description This function check if the device is in the white list +*******************************************************************************/ +BOOLEAN btm_ble_find_dev_in_whitelist(BD_ADDR bd_addr) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + + BTM_TRACE_EVENT0 ("btm_ble_find_dev_in_whitelist"); + + /* empty wl */ + if (p_cb->num_empty_filter == p_cb->max_filter_entries) + { + BTM_TRACE_DEBUG0("white list empty"); + return FALSE; + } + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + if (memcmp(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN) == 0) + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_ble_count_unconn_dev_in_whitelist +** +** Description This function check the number of unconnected device in white list. +*******************************************************************************/ +UINT8 btm_ble_count_unconn_dev_in_whitelist(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 count = 0, i; + BD_ADDR dummy_bda ={0}; + + BTM_TRACE_EVENT0 ("btm_ble_find_dev_in_whitelist"); + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + if (memcmp(p_cb->bg_conn_dev_list[i], dummy_bda, BD_ADDR_LEN) != 0 && + !BTM_IsAclConnectionUp(p_cb->bg_conn_dev_list[i])) + { + count ++; + } + } + return count; +} +/******************************************************************************* +** +** Function btm_update_bg_conn_list +** +** Description This function update the local background connection device list. +*******************************************************************************/ +BOOLEAN btm_update_bg_conn_list(BOOLEAN to_add, BD_ADDR bd_addr) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + BD_ADDR dummy_bda = {0}; + BTM_TRACE_EVENT0 ("btm_update_bg_conn_list"); + if ((to_add && (p_cb->bg_conn_dev_num == BTM_BLE_MAX_BG_CONN_DEV_NUM || p_cb->num_empty_filter == 0)) || + (!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries)) + { + BTM_TRACE_DEBUG1("num_empty_filter = %d", p_cb->num_empty_filter); + return FALSE; + } + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + /* to add */ + if (memcmp(p_cb->bg_conn_dev_list[i], dummy_bda, BD_ADDR_LEN) == 0 && to_add) + { + memcpy(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN); + p_cb->bg_conn_dev_num ++; + return TRUE; + } + /* to remove */ + if (!to_add && memcmp(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN) == 0) + { + memset(p_cb->bg_conn_dev_list[i], 0, BD_ADDR_LEN); + p_cb->bg_conn_dev_num --; + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_write_bg_conn_wl +** +** Description This function write background connection device list into +** controller. +*******************************************************************************/ +void btm_write_bg_conn_wl(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + BTM_TRACE_EVENT0 ("btm_write_bg_conn_wl"); + btm_ble_clear_white_list(); + + for (i = 0; i < p_cb->bg_conn_dev_num; i ++) + { + if (!btm_update_dev_to_white_list(TRUE, p_cb->bg_conn_dev_list[i], BLE_ADDR_PUBLIC)) + break; + } + return; +} +/******************************************************************************* +** +** Function btm_ble_start_auto_conn +** +** Description This function is to start/stop auto connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_start_auto_conn(BOOLEAN start) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BD_ADDR dummy_bda = {0}; + BOOLEAN exec = TRUE; + UINT16 scan_int, scan_win; + + scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_INT : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_WIND : p_cb->scan_win; + + if (start) + { + if (!l2cb.is_ble_connecting && + btm_ble_count_unconn_dev_in_whitelist() > 0) + { + if (p_cb->bg_conn_state != BLE_BG_CONN_ACTIVE && p_cb->bg_conn_dev_num > 0) + { + if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + 0x01, /* UINT8 white_list */ + BLE_ADDR_PUBLIC, /* UINT8 addr_type_peer */ + dummy_bda, /* BD_ADDR bda_peer */ + BLE_ADDR_PUBLIC, /* UINT8 addr_type_own */ + BTM_BLE_CONN_INT_MIN_DEF, /* UINT16 conn_int_min */ + BTM_BLE_CONN_INT_MAX_DEF, /* UINT16 conn_int_max */ + BTM_BLE_CONN_SLAVE_LATENCY_DEF, /* UINT16 conn_latency */ + BTM_BLE_CONN_TIMEOUT_DEF, /* UINT16 conn_timeout */ + 0, /* UINT16 min_len */ + 0)) /* UINT16 max_len */ + { + /* start auto connection failed */ + exec = FALSE; + } + else + { + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + } + } + } + else + exec = FALSE; + } + else + { + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (!btsnd_hcic_ble_create_conn_cancel()) + exec = FALSE; + else + p_cb->bg_conn_state = BLE_BG_CONN_IDLE; + } + } + return exec; +} + +/******************************************************************************* +** +** Function btm_ble_start_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connectino procedure is started. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_select_conn(BOOLEAN start,tBTM_BLE_SEL_CBACK *p_select_cback) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT16 scan_int, scan_win; + + BTM_TRACE_EVENT0 ("btm_ble_start_select_conn"); + + scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_INT : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_WIND : p_cb->scan_win; + + if (start) + { + if (!btm_cb.btm_inq_vars.inq_active) + { + btm_cb.ble_ctr_cb.p_select_cback = p_select_cback; + + btm_update_scanner_filter_policy(SP_ADV_WL); + + if (!btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_PASS, /* use passive scan by default */ + scan_int, /* scan interval */ + scan_win, /* scan window */ + BLE_ADDR_PUBLIC, /* own device, DUMO always use public */ + SP_ADV_WL) /* process advertising packets only from devices in the White List */ + ) + return FALSE; + + if (p_cb->inq_var.adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_ERROR0("peripheral device cannot initiate a selective connection"); + return FALSE; + } + else if (p_cb->bg_conn_dev_num > 0 && btm_ble_count_unconn_dev_in_whitelist() > 0 ) + { + + if (!btsnd_hcic_ble_set_scan_enable(TRUE, TRUE)) /* duplicate filtering enabled */ + return FALSE; + + /* mark up inquiry status flag */ + btm_cb.btm_inq_vars.inq_active = TRUE; + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_SELECT_SCAN; + + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + + } + } + else + { + BTM_TRACE_ERROR0("scan active, can not start selective connection procedure"); + return FALSE; + } + } + else /* disable selective connection mode */ + { + p_cb->p_select_cback = NULL; + btm_cb.btm_inq_vars.inq_active = FALSE; + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_INQUIRY_NONE; + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + /* stop scanning */ + if (p_cb->bg_conn_dev_num > 0) + { + if (!btsnd_hcic_ble_set_scan_enable(FALSE, TRUE)) /* duplicate filtering enabled */ + return FALSE; + } + } + return TRUE; +} +/******************************************************************************* +** +** Function btm_ble_initiate_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connectino procedure is started. +** +*******************************************************************************/ +void btm_ble_initiate_select_conn(BD_ADDR bda) +{ + UINT8 addr_type; + BTM_TRACE_EVENT0 ("btm_ble_initiate_select_conn"); + addr_type = btm_ble_map_bda_to_conn_bda(bda); + + /* use direct connection procedure to initiate connection */ + if (!L2CA_ConnectFixedChnl(L2CAP_ATT_CID, bda)) + { + BTM_TRACE_ERROR0("btm_ble_initiate_select_conn failed"); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_sele_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_suspend_bg_sele_conn(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_suspend_bg_sele_conn"); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_cb->bg_conn_state = BLE_BG_CONN_SUSPEND; + btm_ble_start_select_conn(FALSE, NULL); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_suspend_bg_conn(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_suspend_bg_conn"); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + if (btm_ble_start_auto_conn(FALSE)) + p_cb->bg_conn_state = BLE_BG_CONN_SUSPEND; + } +} +/******************************************************************************* +** +** Function btm_ble_scan_param_idle +** +** Description This function is to process the scan parameter idle timeout +** timeout. +********************************************************************************/ +void btm_ble_scan_param_idle(void) +{ + BTM_BleSetConnScanParams(BTM_BLE_CONN_EST_SCAN_INT_LO, BTM_BLE_CONN_EST_SCAN_WIND_LO); +} +/******************************************************************************* +** +** Function btm_ble_resume_bg_conn +** +** Description This function is to resume a background auto connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +BOOLEAN btm_ble_resume_bg_conn(tBTM_BLE_SEL_CBACK *p_sele_callback, BOOLEAN def_param) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BOOLEAN ret = FALSE; + + if (p_cb->bg_conn_state != BLE_BG_CONN_ACTIVE ) + { + if (def_param) + { + p_cb->scan_int = BTM_BLE_CONN_PARAM_UNDEF; + p_cb->scan_win = BTM_BLE_CONN_PARAM_UNDEF; + + /* start scan param idle timer */ + btu_start_timer(&p_cb->scan_param_idle_timer, + BTU_TTYPE_BLE_SCAN_PARAM_IDLE, + BTM_BLE_SCAN_PARAM_TOUT); + } + + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + ret = btm_ble_start_auto_conn(TRUE); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + /* terminate selective connection mode if all devices are connected */ + if (btm_ble_count_unconn_dev_in_whitelist() == 0) + { + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_DISABLE); + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_INQUIRY_NONE; + btm_cb.btm_inq_vars.inq_active = FALSE; + } + else if (!btm_cb.btm_inq_vars.inq_active) + btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback); + } + + if (ret) + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + + } + + return ret; +} +/******************************************************************************* +** +** Function btm_ble_update_bg_state +** +** Description This function is to update the bg connection status. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_update_bg_state(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (!l2cb.is_ble_connecting && (p_cb->bg_conn_state != BLE_BG_CONN_SUSPEND)) + p_cb->bg_conn_state = BLE_BG_CONN_IDLE; + +} + +#endif + diff --git a/stack/btm/btm_ble_gap.c b/stack/btm/btm_ble_gap.c new file mode 100644 index 0000000..b9e17c0 --- /dev/null +++ b/stack/btm/btm_ble_gap.c @@ -0,0 +1,2084 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE GAP. + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_types.h" +#include "btu.h" +#include "btm_int.h" +#include "hcimsgs.h" +#if (GAP_INCLUDED == TRUE) +#include "gap_api.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#define BTM_BLE_NAME_SHORT 0x01 +#define BTM_BLE_NAME_CMPL 0x02 + +#define BTM_BLE_FILTER_TARGET_UNKNOWN 0xff +#define BTM_BLE_POLICY_UNKNOWN 0xff + +#define BLE_RESOLVE_ADDR_MSB 0x40 /* most significant bit, bit7, bit6 is 01 to be resolvable random */ +#define BLE_RESOLVE_ADDR_MASK 0xc0 /* bit 6, and bit7 */ +#define BTM_BLE_IS_RESOLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB) + +#define BTM_EXT_BLE_RMT_NAME_TIMEOUT 30 +static tBLE_BD_ADDR le_bda_any ={BLE_ADDR_PUBLIC, {0x00,0x00,0x00,0x00,0x00,0x00}}; + + +#define BTM_BLE_VALID_CONN_DIRECT(x) (memcmp(&le_bda_any, x, sizeof(tBLE_BD_ADDR)) != 0) + +/******************************************************************************* +** Local functions +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag); +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p); +static UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); + + + + + + + + +/******************************************************************************* +** +** Function BTM_BleReset +** +** Description This function is called to reset ULP controller. +** +** Parameters None. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleReset(void) +{ + btsnd_hcic_ble_reset(); +} + +/******************************************************************************* +** +** Function BTM_BleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** white_list: use white list in observer mode or not. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT8 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_NO_RESOURCES; + + BTM_TRACE_EVENT0 ("BTM_BleObserve "); + + if (start) + { + if (p_inq->proc_mode != BTM_BLE_INQUIRY_NONE) + return BTM_BUSY; + + btm_cb.btm_inq_vars.p_inq_results_cb = p_results_cb; + btm_cb.btm_inq_vars.p_inq_cmpl_cb = p_cmpl_cb; + + /* allow config scanning type */ + if (btsnd_hcic_ble_set_scan_params ((UINT8)((p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? BTM_BLE_SCAN_MODE_ACTI: p_inq->scan_type), + (UINT16)(!p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval), + (UINT16)(!p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window), + BLE_ADDR_PUBLIC, + BTM_BLE_DEFAULT_SFP)) /* assume observe always not using white list */ + { + /* start scan, disable duplicate filtering */ + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE)) + { + status = BTM_SUCCESS; + p_inq->proc_mode = BTM_BLE_OBSERVE; + btm_cb.btm_inq_vars.inq_active = TRUE; + + if (duration != 0) + { + /* start inquiry timer */ + btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration); + } + } + } + } + else if (p_inq->proc_mode == BTM_BLE_OBSERVE) + { + btm_ble_stop_scan(); + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleBroadcast +** +** Description This function is to start or stop broadcasting. +** +** Parameters start: start or stop broadcasting. +** +** Returns status. +** +*******************************************************************************/ +tBTM_STATUS BTM_BleBroadcast(BOOLEAN start) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 evt_type = p_cb->scan_rsp ? BTM_BLE_DISCOVER_EVT: BTM_BLE_NON_CONNECT_EVT; + +#ifdef BTM_BLE_PC_ADV_TEST_MODE + if (BTM_BLE_PC_ADV_TEST_MODE) + { + evt_type = p_cb->scan_rsp ? BTM_BLE_CONNECT_EVT: BTM_BLE_NON_CONNECT_EVT; + } +#endif + + if (start && p_cb->adv_mode == BTM_BLE_ADV_DISABLE) + { + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_ENABLE; + + status = BTM_SUCCESS; + } + } + else if (!start && p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + + status = BTM_SUCCESS; + } + } + else + { + status = BTM_WRONG_MODE; + BTM_TRACE_ERROR2("Can not %s Broadcast, device %s in Broadcast mode", + (start ? "Start" : "Stop"), (start ? "alerady" :"not")); + } + + return status; +} + + + + + + + +/******************************************************************************* +** +** Function BTM_RegisterScanReqEvt +** +** Description This function is called to register a scan request callback +** on the advertiser. +** +** Parameters p_scan_req_cback: scan request callback. If NULL, remove the +** registration. +** +** Returns void +** +*******************************************************************************/ +void BTM_RegisterScanReqEvt(tBTM_BLE_SCAN_REQ_CBACK *p_scan_req_cback) +{ +#ifdef BTM_BLE_PC_ADV_TEST_MODE // For general stack code (e.g. BTInsight testing), we simply do not define it to exclude or set it to TRUE to include + if (BTM_BLE_PC_ADV_TEST_MODE) // For stack component, it is always defined and maps to a global variable g_bDraculaAdvertisingMode + { + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + p_cb->p_scan_req_cback = p_scan_req_cback; + } +#endif +} +/******************************************************************************* +** +** Function BTM_BleConfigPrivacy +** +** Description This function is called to enable or disable the privacy in +** the local device. +** +** Parameters enable: TRUE to enable it; FALSE to disable it. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleConfigPrivacy(BOOLEAN enable) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 (" BTM_BleConfigPrivacy"); + p_cb->privacy = enable; +} + +/******************************************************************************* +** +** Function BTM_BleSetBgConnType +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters bg_conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE bg_conn_type, + tBTM_BLE_SEL_CBACK *p_select_cback) +{ + BOOLEAN started = TRUE; + + BTM_TRACE_EVENT0 ("BTM_BleSetBgConnType "); + + if (btm_cb.ble_ctr_cb.bg_conn_type != bg_conn_type) + { + switch (bg_conn_type) + { + case BTM_BLE_CONN_AUTO: + btm_ble_start_auto_conn(TRUE); + break; + + case BTM_BLE_CONN_SELECTIVE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) + { + btm_ble_start_auto_conn(FALSE); + } + started = btm_ble_start_select_conn(TRUE, p_select_cback); + break; + + case BTM_BLE_CONN_NONE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) + { + btm_ble_start_auto_conn(FALSE); + } + else if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + btm_ble_start_select_conn(FALSE, NULL); + } + started = TRUE; + break; + + default: + BTM_TRACE_ERROR1("invalid bg connection type : %d ", bg_conn_type); + started = FALSE; + break; + } + + if (started) + btm_cb.ble_ctr_cb.bg_conn_type = bg_conn_type; + } + return started; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateBgConnDev +** +** Description This function is called to add or remove a device into/from +** background connection procedure. The background connection +* procedure is decided by the background connection type, it can be +* auto connection, or selective connection. +** +** Parameters add_remove: TRUE to add; FALSE to remove. +** remote_bda: device address to add/remove. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR remote_bda) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_BLE_SEL_CBACK *p_select_cback; + BOOLEAN ret = TRUE; + + BTM_TRACE_EVENT0 (" BTM_BleUpdateBgConnDev"); + + /* if auto connection is active */ + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + /* terminate auto connection first */ + ret = btm_ble_start_auto_conn(FALSE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = btm_cb.ble_ctr_cb.p_select_cback; + ret = btm_ble_start_select_conn(FALSE, NULL); + } + } + if (ret) + { + /* update white list */ + ret = btm_update_bg_conn_list(add_remove, remote_bda); + btm_update_dev_to_white_list(add_remove, remote_bda, BLE_ADDR_PUBLIC); + } + + if (ret && p_cb->bg_conn_state == BLE_BG_CONN_IDLE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + /* restart auto connection */ + btm_ble_start_auto_conn(TRUE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = btm_cb.ble_ctr_cb.p_select_cback; + btm_ble_start_select_conn(TRUE, p_select_cback); + } + } + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleSetConnMode +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters directed: is directed connectable mode, or non-directed. +** p_dir_bda: connectable direct initiator's LE device address +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetConnMode(BOOLEAN directed, tBLE_BD_ADDR *p_dir_bda) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR reconn_bda; + + BTM_TRACE_EVENT0 ("BTM_BleSetConnMode "); + + memcpy(&p_cb->direct_bda, &le_bda_any, sizeof(tBLE_BD_ADDR)); + p_cb->own_addr_type = BLE_ADDR_PUBLIC; + + if (directed) + { + if (p_dir_bda) + { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + + if (btm_cb.ble_ctr_cb.privacy /* && GAP privacy ad reconnect addr exist */) + { + /* write reconnect address to controller*/ + btsnd_hcic_ble_set_random_addr(reconn_bda); + } + /* else use static address or publich address */ + + } + else + status = BTM_ILLEGAL_VALUE; + } + else /* undirected connecatable */ + { + if (btm_cb.ble_ctr_cb.privacy /* GAP privacy flag enabled */) + { + /* generate resolvable private address */ + btm_gen_resolvable_private_addr(); + } /* else use publich address */ + + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParams(UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT0 ("BTM_BleSetAdvParams"); + + if (!BTM_BLE_VALID_PRAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_VALID_PRAM(adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) + { + return BTM_ILLEGAL_VALUE; + } + + p_cb->adv_interval_min = adv_int_min; + p_cb->adv_interval_max = adv_int_max; + p_cb->adv_chnl_map = chnl_map; + + if (p_dir_bda) + { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } + else + memcpy(&p_cb->direct_bda, &le_bda_any, sizeof(tBLE_BD_ADDR)); + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_EVENT0 ("update params for an active adv"); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) + { + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + p_cb->evt_type = BTM_BLE_CONNECT_DIR_EVT; + else + p_cb->evt_type = BTM_BLE_CONNECT_EVT; + + BTM_TRACE_DEBUG1(" evt_type = %d", p_cb->evt_type); + } + + if (!btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + status = BTM_NO_RESOURCES; + else + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params (p_cb->adv_interval_min, + p_cb->adv_interval_max, + p_cb->evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, + p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + + else if (!btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + + status = BTM_NO_RESOURCES; + } + + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleReadAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleReadAdvParams (UINT16 *adv_int_min, UINT16 *adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP *p_chnl_map) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT0 ("BTM_BleReadAdvParams "); + + *adv_int_min = p_cb->adv_interval_min; + *adv_int_max = p_cb->adv_interval_max; + *p_chnl_map = p_cb->adv_chnl_map; + + if (p_dir_bda != NULL) + { + memcpy(p_dir_bda, &p_cb->direct_bda, sizeof(tBLE_BD_ADDR)); + } +} + + +/******************************************************************************* +** +** Function BTM_BleSetScanParams +** +** Description This function is called to set Scan parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** scan_type: active scan or passive scan +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetScanParams(UINT16 scan_interval, UINT16 scan_window, tBTM_BLE_SCAN_MODE scan_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT0 (" BTM_BleSetScanParams"); + + if (BTM_BLE_VALID_PRAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_VALID_PRAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS)) + { + p_cb->scan_type = scan_mode; + + if (BTM_BLE_CONN_PARAM_UNDEF != scan_interval) + p_cb->scan_interval = scan_interval; + + if (BTM_BLE_CONN_PARAM_UNDEF != scan_window) + p_cb->scan_window = scan_window; + } + else + { + BTM_TRACE_ERROR2("Illegal params: scan_interval = %d scan_window = %d", + scan_interval, scan_window); + } + +} + +/******************************************************************************* +** +** Function BTM_BleWriteScanRsp +** +** Description This function is called to write LE scan response. +** +** Parameters: p_scan_rsp: scan response information. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 rsp_data[BTM_BLE_AD_DATA_LEN], + *p = rsp_data; + + BTM_TRACE_EVENT0 (" BTM_BleWriteScanRsp"); + memset(rsp_data, 0, BTM_BLE_AD_DATA_LEN); + btm_ble_build_adv_data(&data_mask, &p, p_data); + + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)(p - rsp_data), rsp_data)) + { + status = BTM_SUCCESS; + + if (p_data != NULL) + btm_cb.ble_ctr_cb.inq_var.scan_rsp = TRUE; + else + btm_cb.ble_ctr_cb.inq_var.scan_rsp = FALSE; + } + else + status = BTM_ILLEGAL_VALUE; + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleWriteAdvData +** +** Description This function is called to write advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_cb_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + UINT16 mask = data_mask; + + memset(p_cb_data, 0, sizeof(tBTM_BLE_LOCAL_ADV_DATA)); + p = p_cb_data->ad_data; + p_cb_data->data_mask = data_mask; + + BTM_TRACE_EVENT0 ("BTM_BleWriteAdvData "); + p_cb_data->p_flags = btm_ble_build_adv_data(&mask, &p, p_data); + + p_cb_data->p_pad = p; + + if (data_mask != 0) + { + BTM_TRACE_ERROR0("Partial data write into ADV"); + } + + p_cb_data->data_mask &= ~mask; + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_cb_data->p_pad - p_cb_data->ad_data), + p_cb_data->ad_data)) + return BTM_SUCCESS; + else + return BTM_NO_RESOURCES; + +} + +/******************************************************************************* +** +** Function BTM_CheckAdvData +** +** Description This function is called to get ADV data for a specific type. +** +** Parameters p_adv - pointer of ADV data +** type - finding ADV data type +** p_length - return the length of ADV data not including type +** +** Returns pointer of ADV data +** +*******************************************************************************/ +UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT8 type, UINT8 *p_length) +{ + UINT8 *p = p_adv; + UINT8 length; + UINT8 adv_type; + BTM_TRACE_API1("BTM_CheckAdvData type=0x%02X", type); + + STREAM_TO_UINT8(length, p); + + while ( length && (p - p_adv <= BTM_BLE_CACHE_ADV_DATA_MAX)) + { + STREAM_TO_UINT8(adv_type, p); + + if ( adv_type == type ) + { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + p += length - 1; /* skip the length of data */ + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +} + +/******************************************************************************* +** +** Function btm_ble_build_adv_data +** +** Description This function is called build the adv data and rsp data. +*******************************************************************************/ +static UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data) +{ + UINT16 data_mask = *p_data_mask; + UINT8 *p = *p_dst, + *p_flag = NULL; + UINT16 len = BTM_BLE_AD_DATA_LEN, cp_len = 0; + UINT8 i = 0; + tBTM_BLE_ATTR *p_attr; + tBTM_BLE_PROP_ELEM *p_elem; + + BTM_TRACE_EVENT0 (" btm_ble_build_adv_data"); + + /* build the adv data structure and build the data string */ + if (data_mask) + { + /* flags */ + if (data_mask & BTM_BLE_AD_BIT_FLAGS) + { + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_flag = p; + if (p_data) + *p++ = p_data->flag; + else + *p++ = 0; + + len -= 3; + + data_mask &= ~BTM_BLE_AD_BIT_FLAGS; + } + /* device name */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_DEV_NAME) + { + if (strlen(btm_cb.cfg.bd_name) > (UINT16)(len - 2)) + { + *p++ = len - 2 + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_SHORT; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, len - 2); + } + else + { + cp_len = (UINT16)strlen(btm_cb.cfg.bd_name); + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_CMPL; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, cp_len); + } + + len -= (cp_len + 2); + data_mask &= ~BTM_BLE_AD_BIT_DEV_NAME; + } + /* manufacturer data */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_MANU && + p_data && p_data->manu.len != 0 && p_data->manu.p_val) + { + if (p_data->manu.len > (len - 2)) + cp_len = len - 2; + else + cp_len = p_data->manu.len; + + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_MANU; + ARRAY_TO_STREAM(p, p_data->manu.p_val, cp_len); + + len -= (cp_len + 2); + data_mask &= ~BTM_BLE_AD_BIT_MANU; + } + /* TX power */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_TX_PWR) + { + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_TX_PWR; + *p++ = btm_cb.ble_ctr_cb.inq_var.tx_power; + len -= 3; + + data_mask &= ~BTM_BLE_AD_BIT_TX_PWR; + } + /* services */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_SERVICE && + p_data && p_data->services.num_service != 0 && + p_data->services.p_uuid) + { + if (p_data->services.num_service * 2 > (len - 2)) + { + cp_len = (len - 2)/2; + *p ++ = 1 + cp_len * 2; + *p++ = BTM_BLE_AD_TYPE_SRV_PART; + } + else + { + cp_len = p_data->services.num_service; + *p++ = 1 + cp_len * 2; + *p++ = BTM_BLE_AD_TYPE_SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) + { + UINT16_TO_STREAM(p, *(p_data->services.p_uuid + i)); + } + + len -= (cp_len * 2 + 2); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE; + } + if (len >= 6 && data_mask & BTM_BLE_AD_BIT_INT_RANGE && + p_data) + { + *p++ = 5; + *p++ = BTM_BLE_AD_TYPE_INT_RANGE; + UINT16_TO_STREAM(p, p_data->int_range.low); + UINT16_TO_STREAM(p, p_data->int_range.hi); + len -= 6; + data_mask &= ~BTM_BLE_AD_BIT_INT_RANGE; + } + if (data_mask & BTM_BLE_AD_BIT_ATTR && p_data && p_data->attr.num_attr != 0) + { + for (i = 0; i < p_data->attr.num_attr ; i ++) + { + p_attr = p_data->attr.attr_list + i; + + if (len >= (2 + 2 + p_attr->data_len))/* len byte(1) + ATTR type(1) + Uuid len(2) + value length */ + { + *p ++ = p_attr->data_len + 2 + 1; /* Uuid len + value length */ + *p ++ = BTM_BLE_AD_TYPE_ATTR; + UINT16_TO_STREAM(p, p_attr->uuid); + ARRAY_TO_STREAM(p, p_attr->p_data, p_attr->data_len); + + len -= (4 + p_attr->data_len); + } + else + break; + } + if (i == p_data->attr.num_attr) + data_mask &= ~BTM_BLE_AD_BIT_ATTR; + } + if (data_mask & BTM_BLE_AD_BIT_PROPRIETARY && p_data && p_data->p_proprietary) + { + for (i = 0; i < p_data->p_proprietary->num_elem ; i ++) + { + p_elem = p_data->p_proprietary->p_elem + i; + + if (len >= (2 + p_elem->len))/* len byte(1) + ATTR type(1) + Uuid len(2) + value length */ + { + *p ++ = p_elem->len + 1; /* Uuid len + value length */ + *p ++ = p_elem->adv_type; + ARRAY_TO_STREAM(p, p_elem->p_val, p_elem->len); + + len -= (2 + p_elem->len); + } + else + { + BTM_TRACE_WARNING0("data exceed max adv packet length"); + break; + } + } + data_mask &= ~BTM_BLE_AD_BIT_PROPRIETARY; + } + } + + *p_data_mask = data_mask; + *p_dst = p; + + return p_flag; +} + +/******************************************************************************* +** +** Function btm_ble_set_discoverability +** +** Description This function is called to set BLE discoverable mode. +** +** Parameters: mode: discoverability mode. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_DISCOVERABLE_MASK); + UINT8 flag = 0; + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type = (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) ? \ + ((p_cb->scan_rsp) ? BTM_BLE_DISCOVER_EVT : BTM_BLE_NON_CONNECT_EVT )\ + : BTM_BLE_CONNECT_EVT; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT2 ("btm_ble_set_discoverability mode=0x%0x combined_mode=0x%x", mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_DISCOVERABLE) + return(BTM_ILLEGAL_VALUE); + + p_cb->br_edr_supported_flag |= (combined_mode & BTM_DISCOVERABLE_MASK); + p_cb->discoverable_mode = mode; + + if (!p_cb->br_edr_supported_flag) + { + flag = BTM_BLE_BREDR_NOT_SPT; + BTM_TRACE_DEBUG1("btm_ble_set_discoverability (BREDR not sup)flag=0x%x",flag); + } + + BTM_TRACE_DEBUG1 ("br_edr_supported=0x%x", p_cb->br_edr_supported_flag); + + if (mode == BTM_BLE_LIMITED_DISCOVERABLE || mode == BTM_BLE_GENERAL_DISCOVERABLE) + { + BTM_TRACE_EVENT0 ("mode == BTM_BLE_LIMITED_DISCOVERABLE "); + /* write ADV data with limited disc flag */ + if (mode == BTM_BLE_LIMITED_DISCOVERABLE) + flag |= BTM_BLE_LIMIT_DISC_FLAG ; + else + flag |= BTM_BLE_GEN_DISC_FLAG; + } + else /* non-discoverable */ + { + BTM_TRACE_EVENT0 ("mode == BTM_BLE_NON_DISCOVERABLE "); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) + { + p_cb->br_edr_supported_flag = 0; + + BTM_TRACE_EVENT0 ("always disable adv in non-discoverable non-connectable mode if no scan rsp "); + if (!p_cb->scan_rsp ) + new_mode = BTM_BLE_ADV_DISABLE; + } + else + { + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + { + BTM_TRACE_DEBUG0("evt_type = BTM_BLE_CONNECT_DIR_EVT"); + evt_type = BTM_BLE_CONNECT_DIR_EVT; + } + else + { + BTM_TRACE_DEBUG0(" evt_type = BTM_BLE_CONNECT_EVT"); + evt_type = BTM_BLE_CONNECT_EVT; + } + } + } + btm_ble_update_adv_flag(flag); + + /* update adv params if start advertising */ + BTM_TRACE_EVENT2 ("evt_type=0x%x p-cb->evt_type=0x%x ", evt_type, p_cb->evt_type); + if (new_mode == BTM_BLE_ADV_ENABLE && evt_type != p_cb->evt_type) + { + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_EVENT0 ("Set Adv disable"); + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + + } + if (status == BTM_SUCCESS && p_cb->adv_mode != new_mode) + { + /* update advertising mode */ + if (!btsnd_hcic_ble_set_adv_enable (new_mode)) + status = BTM_NO_RESOURCES; + else + p_cb->adv_mode = new_mode; + } + + /* set up stop advertising timer */ + if (status == BTM_SUCCESS && mode == BTM_BLE_LIMITED_DISCOVERABLE) + { + BTM_TRACE_EVENT1 ("start timer for limited disc mode duration=%d (30 secs)", BTM_BLE_GAP_LIM_TOUT); + /* start Tgap(lim_timeout) */ + btu_start_timer (&p_cb->inq_timer_ent, BTU_TTYPE_BLE_GAP_LIM_DISC, + BTM_BLE_GAP_LIM_TOUT); + } + return status; +} + +/******************************************************************************* +** +** Function btm_ble_set_connectability +** +** Description This function is called to set BLE connectability mode. +** +** Parameters: mode: connectability mode. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_CONNECTABLE_MASK); + UINT8 cur_flag = 0; + UINT8 cur_br_edr_not_sup_flag; + UINT8 new_flag; + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type = (p_cb->scan_rsp) ? BTM_BLE_DISCOVER_EVT: BTM_BLE_NON_CONNECT_EVT; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT2 ("btm_ble_set_connectability mode=0x%0x combined_mode=0x%x", mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_CONNECTABLE) + return(BTM_ILLEGAL_VALUE); + if (btm_cb.ble_ctr_cb.inq_var.adv_data.p_flags) + cur_flag = *btm_cb.ble_ctr_cb.inq_var.adv_data.p_flags ; + cur_br_edr_not_sup_flag = (cur_flag & ((UINT8) BTM_BLE_BREDR_NOT_SPT)); + + p_cb->br_edr_supported_flag |= ((combined_mode & BTM_CONNECTABLE_MASK) << 4); + if (p_cb->br_edr_supported_flag && cur_br_edr_not_sup_flag) + { + new_flag = cur_flag & ((UINT8) (~BTM_BLE_BREDR_NOT_SPT)); + BTM_TRACE_EVENT2 ("new flag=0x%x cur flag=0x%x",new_flag, cur_flag); + btm_ble_update_adv_flag(new_flag); + } + p_cb->connectable_mode = mode; + + if (mode == BTM_BLE_NON_CONNECTABLE) + { + if (p_cb->discoverable_mode == BTM_BLE_NON_DISCOVERABLE) + { + p_cb->br_edr_supported_flag = 0; + BTM_TRACE_EVENT0 ("always disable adv in non-discoverable non-connectable mode with no scan rsp"); + if(!p_cb->scan_rsp) + new_mode = BTM_BLE_ADV_DISABLE; + } + } + else /* connectable */ + { + BTM_TRACE_DEBUG2("btm_ble_set_connectability: mode = %04x discoverable_mode= %02x", mode, p_cb->discoverable_mode); + + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + { + BTM_TRACE_DEBUG0("evt_type = BTM_BLE_CONNECT_DIR_EVT"); + evt_type = BTM_BLE_CONNECT_DIR_EVT; + } + else + { + BTM_TRACE_DEBUG0(" evt_type = BTM_BLE_CONNECT_EVT"); + evt_type = BTM_BLE_CONNECT_EVT; + } + } + + /* update adv params if needed */ + if (p_cb->evt_type != evt_type && new_mode == BTM_BLE_ADV_ENABLE) + { + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, + p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + } + /* update advertising mode */ + if (status == BTM_SUCCESS && new_mode != p_cb->adv_mode) + { + if (btsnd_hcic_ble_set_adv_enable (new_mode)) + { + status = BTM_SUCCESS; + + p_cb->adv_mode = new_mode; + } + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_start_inquiry +** +** Description This function is called to start BLE inquiry procedure. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Parameters: mode - GENERAL or LIMITED inquiry +** p_inq_params - pointer to the BLE inquiry parameter. +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** p_cmpl_cb - callback indicating the end of an inquiry +** +** +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_DEBUG2("btm_ble_start_inquiry: mode = %02x inq_active = %d", mode, btm_cb.btm_inq_vars.inq_active); + + if (p_inq->proc_mode != BTM_BLE_INQUIRY_NONE) + { + BTM_TRACE_ERROR0("LE scan is active, can not start inquiry"); + return(BTM_BUSY); + } + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + /* start scan, already enable duplicate filtering */ + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE)) + { + status = BTM_SUCCESS; + p_inq->proc_mode = mode; + + if (duration != 0) + { + /* start inquiry timer */ + btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration); + } + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name_cmpl +** +** Description This function is called when BLE remote name is received. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_name_cmpl(BOOLEAN status, BD_ADDR bda, UINT16 length, char *p_name) +{ + UINT8 hci_status = HCI_SUCCESS; + BD_NAME bd_name; + + memset(bd_name, 0, BD_NAME_LEN); + memcpy((UINT8*)bd_name, p_name, length); + + if ((!status) || (length==0)) + { + hci_status = HCI_ERR_HOST_TIMEOUT; + } + + btm_process_remote_name(bda, bd_name, length, hci_status); + btm_sec_rmt_name_request_complete (bda, (UINT8 *)p_name, hci_status); +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name +** +** Description This function read remote LE device name using GATT read +** procedure. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_cur && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_ADV && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_DIR_ADV) + { + BTM_TRACE_DEBUG0("name request to non-connectable device failed."); + return BTM_ERR_PROCESSING; + } + + /* read remote device name using GATT procedure */ + if (p_inq->remname_active) + return BTM_BUSY; + + if (!GAP_BleReadPeerDevName(remote_bda, btm_ble_read_remote_name_cmpl)) + return BTM_BUSY; + + p_inq->p_remname_cmpl_cb = p_cb; + p_inq->remname_active = TRUE; + + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + BTM_EXT_BLE_RMT_NAME_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_cancel_remote_name +** +** Description This function cancel read remote LE device name. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN status; + + status = GAP_BleCancelReadPeerDevName(remote_bda); + + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + btu_stop_timer(&p_inq->rmt_name_timer_ent); + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_update_adv_flag +** +** Description This function update the limited discoverable flag in the adv +** data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + + BTM_TRACE_DEBUG1 ("btm_ble_update_adv_flag new=0x%x", flag); + + if (p_adv_data->p_flags != NULL) + { + BTM_TRACE_DEBUG1 ("btm_ble_update_adv_flag old=0x%x", *p_adv_data->p_flags); + *p_adv_data->p_flags = flag; + } + else /* no FLAGS in ADV data*/ + { + p = (p_adv_data->p_pad == NULL) ? p_adv_data->ad_data : p_adv_data->p_pad; + /* need 3 bytes space to stuff in the flags, if not */ + /* erase all written data, just for flags */ + if ((BTM_BLE_AD_DATA_LEN - (p - p_adv_data->ad_data)) < 3) + { + p = p_adv_data->p_pad = p_adv_data->ad_data; + memset(p_adv_data->ad_data, 0, BTM_BLE_AD_DATA_LEN); + } + + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_adv_data->p_flags = p; + *p++ = flag; + p_adv_data->p_pad = p; + } + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_adv_data->p_pad - p_adv_data->ad_data), + p_adv_data->ad_data)) + p_adv_data->data_mask |= BTM_BLE_AD_BIT_FLAGS; + +} + +#if 0 +/******************************************************************************* +** +** Function btm_ble_parse_adv_data +** +** Description This function parse the adv data into a structure. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +static void btm_ble_parse_adv_data(tBTM_INQ_INFO *p_info, UINT8 *p_data, + UINT8 len, tBTM_BLE_INQ_DATA *p_adv_data, UINT8 *p_buf) +{ + UINT8 *p_cur = p_data; + UINT8 ad_len, ad_type, ad_flag; + tBTM_BLE_ATTR *p_attr; + + BTM_TRACE_EVENT0 (" btm_ble_parse_adv_data"); + + while (len > 0) + { + BTM_TRACE_DEBUG1("btm_ble_parse_adv_data: len = %d", len); + if ((ad_len = *p_cur ++) == 0) + break; + + ad_type = *p_cur ++; + + BTM_TRACE_DEBUG2(" ad_type = %02x ad_len = %d", ad_type, ad_len); + + switch (ad_type) + { + case BTM_BLE_AD_TYPE_NAME_SHORT: + + case BTM_BLE_AD_TYPE_NAME_CMPL: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_DEV_NAME; + if (p_info) + { + p_info->remote_name_type =(ad_type == BTM_BLE_AD_TYPE_NAME_SHORT) ? + BTM_BLE_NAME_SHORT: BTM_BLE_NAME_CMPL; + memcpy(p_info->remote_name, p_cur, ad_len -1); + p_info->remote_name[ad_len] = 0; + p_adv_data->p_remote_name = p_info->remote_name; + p_info->remote_name_len = p_adv_data->remote_name_len = ad_len - 1; + BTM_TRACE_DEBUG1("BTM_BLE_AD_TYPE_NAME name = %s",p_adv_data->p_remote_name); + } + p_cur += (ad_len -1); + + break; + + case BTM_BLE_AD_TYPE_FLAG: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_FLAGS; + ad_flag = *p_cur ++; + p_adv_data->flag = (UINT8)(ad_flag & BTM_BLE_ADV_FLAG_MASK) ; + BTM_TRACE_DEBUG3("BTM_BLE_AD_TYPE_FLAG flag = %s | %s | %s", + (p_adv_data->flag & BTM_BLE_LIMIT_DISC_FLAG)? "LE_LIMIT_DISC" : "", + (p_adv_data->flag & BTM_BLE_GEN_DISC_FLAG)? "LE_GENERAL_DISC" : "", + (p_adv_data->flag & BTM_BLE_BREDR_NOT_SPT)? "LE Only device" : ""); + break; + + case BTM_BLE_AD_TYPE_TX_PWR: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_TX_PWR; + p_adv_data->tx_power_level = (INT8)*p_cur ++; + BTM_TRACE_DEBUG1("BTM_BLE_AD_TYPE_TX_PWR tx_level = %d", p_adv_data->tx_power_level); + break; + + case BTM_BLE_AD_TYPE_ATTR: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_ATTR; + p_attr = &p_adv_data->attr_data.attr_list[p_adv_data->attr_data.num_attr]; + p_attr->uuid = *p_cur ++; + + if (ad_len > 3) + { + p_attr->data_len = ad_len - 3; + p_attr->p_data = p_buf; + memcpy(p_attr->p_data, p_cur, p_attr->data_len); + p_buf += p_attr->data_len; + } + + p_adv_data->attr_data.num_attr ++; + BTM_TRACE_DEBUG2("BTM_BLE_AD_TYPE_ATTR[%d] uuid = 0x%04x",p_adv_data->attr_data.num_attr, p_attr->uuid); + break; + + case BTM_BLE_AD_TYPE_MANU: + + case BTM_BLE_AD_TYPE_SRV_CMPL: + case BTM_BLE_AD_TYPE_SRV_PART: + p_adv_data->ad_mask |= ad_type; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1)/2; + BTM_TRACE_DEBUG1("service UUID list, num = %d", p_adv_data->service.num_service); + + default: + p_cur += (ad_len - 1); + break; + } + len -= (ad_len + 1); + } +} +#endif + +/******************************************************************************* +** +** Function btm_ble_cache_adv_data +** +** Description Update advertising cache data. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_cache_adv_data(tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, UINT8 evt_type) +{ + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p_cache; + UINT8 length; + + /* cache adv report/scan response data */ + if (evt_type != BTM_BLE_SCAN_RSP_EVT) + { + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + } + + if (data_len > 0) + { + p_cache = &p_le_inq_cb->adv_data_cache[p_le_inq_cb->adv_len]; + STREAM_TO_UINT8(length, p); + while ( length && ((p_le_inq_cb->adv_len + length + 1) <= BTM_BLE_CACHE_ADV_DATA_MAX)) + { + /* copy from the length byte & data into cache */ + memcpy(p_cache, p-1, length+1); + /* advance the cache pointer past data */ + p_cache += length+1; + /* increment cache length */ + p_le_inq_cb->adv_len += length+1; + /* skip the length of data */ + p += length; + STREAM_TO_UINT8(length, p); + } + } + + /* parse service UUID from adv packet and save it in inq db eir_uuid */ + /* TODO */ +} +/******************************************************************************* +** +** Function btm_ble_is_discoverable +** +** Description check ADV flag to make sure device is discoverable and match +** the search condition +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p) +{ + BOOLEAN is_discoverable = FALSE; + UINT8 *p_flag, flag = 0; + UINT8 data_len; + tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms; + + STREAM_TO_UINT8 (data_len, p); + + /* for observer, always "discoverable */ + if (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_OBSERVE || + (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_SELECT_SCAN && + btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE)) + return TRUE; + + /* does not match filter condition */ + if (p_cond->filter_cond_type == BTM_FILTER_COND_BD_ADDR && + memcmp(bda, p_cond->filter_cond.bdaddr_cond, BD_ADDR_LEN) != 0) + { + BTM_TRACE_DEBUG0("BD ADDR does not meet filter condition"); + return FALSE; + } + + /* scan response does not include the flag */ + if (evt_type == BTM_BLE_SCAN_RSP_EVT) + return FALSE; + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) + { + BTM_TRACE_WARNING1("ADV data too long %d. discard", data_len); + return FALSE; + } + + if (data_len != 0) + { + if ((p_flag = BTM_CheckAdvData(p, BTM_BLE_AD_TYPE_FLAG, &data_len)) != NULL) + { + flag = * p_flag; + + if ((btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_GENERAL_INQUIRY) && + (flag & (BTM_BLE_LIMIT_DISC_FLAG|BTM_BLE_GEN_DISC_FLAG)) != 0) + { + BTM_TRACE_DEBUG0("Find Generable Discoverable device"); + is_discoverable = TRUE; + } + + else if (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_LIMITED_INQUIRY && + (flag & BTM_BLE_LIMIT_DISC_FLAG) != 0) + { + BTM_TRACE_DEBUG0("Find limited discoverable device"); + is_discoverable = TRUE; + } + + } + } + + if (!is_discoverable) + { + BTM_TRACE_ERROR1("discoverable flag not desired: %d", flag); + } + + return is_discoverable; +} +/******************************************************************************* +** +** Function btm_ble_update_inq_result +** +** Description Update adv packet information into inquiry result. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + BOOLEAN to_report = TRUE; + tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results; + UINT8 len; + UINT8 *p_flag; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 data_len, rssi; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p1; + + STREAM_TO_UINT8 (data_len, p); + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) + { + BTM_TRACE_WARNING1("EIR data too long %d. discard", data_len); + return FALSE; + } + btm_ble_cache_adv_data(p_cur, data_len, p, evt_type); + + p1 = (p + data_len); + STREAM_TO_UINT8 (rssi, p1); + + /* Save the info */ + p_cur->inq_result_type = BTM_INQ_RESULT_BLE; + p_cur->ble_addr_type = addr_type; + p_cur->rssi = rssi; + + /* active scan, always wait until get scan_rsp to report the result */ + if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI && + (evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT))) + { + p_i->scan_rsp = FALSE; + to_report = FALSE; + } + else + p_i->scan_rsp = TRUE; + + if (p_i->inq_count != p_inq->inq_counter) + p_cur->device_type = BT_DEVICE_TYPE_BLE; + else + p_cur->device_type |= BT_DEVICE_TYPE_BLE; + + if (evt_type != BTM_BLE_SCAN_RSP_EVT) + p_cur->ble_evt_type = evt_type; + + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + + if (p_le_inq_cb->adv_len != 0) + { + if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_FLAG, &len)) != NULL) + p_cur->flag = * p_flag; + } + + /* if BR/EDR not supported is not set, assume is a DUMO device */ + if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == 0) + { + BTM_TRACE_ERROR0("BR/EDR NOT support bit not set, treat as DUMO"); + p_cur->device_type |= BT_DEVICE_TYPE_DUMO; + } + else + { + BTM_TRACE_DEBUG0("BR/EDR NOT SUPPORT bit set, LE only device"); + } + + return to_report; + +} + +/******************************************************************************* +** +** Function btm_send_sel_conn_callback +** +** Description send selection connection request callback. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_send_sel_conn_callback(BD_ADDR remote_bda, UINT8 evt_type, UINT8 *p_data, UINT8 addr_type) +{ + UINT8 data_len, len; + UINT8 *p_dev_name, remname[31] = {0}; + + if (btm_cb.ble_ctr_cb.p_select_cback == NULL || + /* non-connectable device */ + (evt_type != BTM_BLE_EVT_CONN_ADV && evt_type != BTM_BLE_EVT_CONN_DIR_ADV)) + return; + + STREAM_TO_UINT8 (data_len, p_data); + + /* get the device name if exist in ADV data */ + if (data_len != 0) + { + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_CMPL, &len); + + if (p_dev_name == NULL) + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_SHORT, &len); + + if (p_dev_name) + memcpy(remname, p_dev_name, len); + } + /* allow connection */ + if ((* btm_cb.ble_ctr_cb.p_select_cback)(remote_bda, remname)) + { + /* terminate selective connection, initiate connection */ + btm_ble_initiate_select_conn(remote_bda); + } +} + +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_cmpl +** +** Description resolve random address complete callback. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_cmpl(void * p_rec, void *p) +{ + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + UINT8 addr_type = BLE_ADDR_RANDOM; + BD_ADDR bda; + UINT8 *pp = (UINT8 *)p + 1; + UINT8 evt_type; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_random_addr_cmpl "); + + STREAM_TO_UINT8 (evt_type, pp); + STREAM_TO_UINT8 (addr_type, pp); + STREAM_TO_BDADDR (bda, pp); + + if (match_rec) + { + BTM_TRACE_ERROR0("Random match"); + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } + else + { + BTM_TRACE_ERROR0("Random unmatch"); + } + + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, pp); + + return; +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt +** +** Description This function is called when adv packet report events are +** received from the device. It updates the inquiry database. +** If the inquiry database is full, the oldest entry is discarded. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_process_adv_pkt (UINT8 *p_data) +{ + BD_ADDR bda; + UINT8 evt_type = 0, *p = p_data; + UINT8 addr_type = 0; + + BTM_TRACE_EVENT0 ("btm_ble_process_adv_pkt "); + + /* always get one device at a time */ + p ++; + + /* Extract inquiry results */ + STREAM_TO_UINT8 (evt_type, p); + STREAM_TO_UINT8 (addr_type, p); + STREAM_TO_BDADDR (bda, p); + +#ifdef BTM_BLE_PC_ADV_TEST_MODE // For general stack code (e.g. BTInsight testing), we simply do not define it to exclude or set it to TRUE to include + if (BTM_BLE_PC_ADV_TEST_MODE) // For stack component, it is always defined and maps to a global variable g_bDraculaAdvertisingMode + { + if (btm_cb.ble_ctr_cb.p_scan_req_cback) + (*btm_cb.ble_ctr_cb.p_scan_req_cback)(bda, addr_type, evt_type); + } +#endif + + /* Only process the results if the inquiry is still active */ + if (!btm_cb.btm_inq_vars.inq_active && + (btm_cb.ble_ctr_cb.bg_conn_type != BTM_BLE_CONN_SELECTIVE || + /* or selective auto connection is active */ + btm_cb.ble_ctr_cb.p_select_cback == NULL)) + return; + + +#if SMP_INCLUDED == TRUE + if (addr_type == BLE_ADDR_RANDOM && BTM_BLE_IS_RESOLVE_BDA(bda)) + { + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_cmpl, p_data); + } + else +#endif + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, p); +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt_cont +** +** Description This function is called after random address resolution is +** done, and proceed to process adv packet. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + tINQ_DB_ENT *p_i; + BOOLEAN to_report = FALSE; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + p_i = btm_inq_db_find (bda); + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) + { + /* never been report as an LE device */ + if ((p_i && + (!(p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) || + /* scan repsonse to be updated */ + (!p_i->scan_rsp))) + || + btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_OBSERVE) + { + BTM_TRACE_DEBUG0("update new BLE information "); + to_report = TRUE; + } + else + { + BTM_TRACE_DEBUG0("LE in le_bd_db already"); + /* if yes, skip it */ + return; /* assumption: one result per event */ + } + } + else /* not been processed int his round */ + { + BTM_TRACE_DEBUG0("new LE BD_ADDR"); + to_report = TRUE; + } + + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) + { + if (btm_ble_is_discoverable(bda, evt_type, p)) + { + if ((p_i = btm_inq_db_new (bda)) != NULL) + { + p_inq->inq_cmpl_info.num_resp++; + BTM_TRACE_DEBUG0("adv pkt process: new record is added into inq db"); + to_report = TRUE; + } + else + return; + } + else + { + BTM_TRACE_ERROR0("discard adv pkt"); + return; + } + } + else if (p_i->inq_count != p_inq->inq_counter) /* first time seen in this inquiry */ + { + p_inq->inq_cmpl_info.num_resp++; + } + + /* update the LE device information in inquiry database */ + if (to_report) + { + to_report = btm_ble_update_inq_result(p_i, addr_type, evt_type, p); + } + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* If the number of responses found and limited, issue a cancel inquiry */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps) + { + /* new device */ + if (p_i == NULL || + (/* assume a DUMO device, BR/EDR inquiry is always active */ + p_i && p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BLE && p_i->scan_rsp)) + { + BTM_TRACE_WARNING0("INQ RES: Extra Response Received...cancelling inquiry.."); + + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) ) + btsnd_hcic_inq_cancel(); + + /* stop LE scan now */ + btm_ble_stop_scan(); + +#if BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + } + } +#endif + + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + btm_send_sel_conn_callback(bda, evt_type, p, addr_type); + } + else if (p_inq_results_cb && to_report) + { + BTM_TRACE_DEBUG0("BTMINQ LE: Found devices, send callback btm_inqrslt_cb"); + + if (p_inq->inq_active) + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + } +} + +/******************************************************************************* +** +** Function btm_ble_stop_scan +** +** Description Stop the BLE scan. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_scan(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_EVENT0 ("btm_ble_stop_scan "); + + btu_stop_timer (&p_cb->inq_timer_ent); + + /* Clear the inquiry callback if set */ + p_cb->scan_type = BTM_BLE_SCAN_MODE_NONE; + p_cb->proc_mode = BTM_BLE_INQUIRY_NONE; + + /* stop discovery now */ + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG2 ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + btm_process_inq_complete(HCI_SUCCESS, (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK)); + +} + +/******************************************************************************* +** +** Function btm_ble_stop_adv +** +** Description Stop the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_adv(void) +{ + BTM_TRACE_EVENT0 (" btm_ble_stop_adv"); + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + } +} + +/******************************************************************************* +** +** Function btm_ble_timeout +** +** Description Called when BTM BLE inquiry timer expires +** +** Returns void +** +*******************************************************************************/ +void btm_ble_timeout(TIMER_LIST_ENT *p_tle) +{ + BTM_TRACE_EVENT0 ("btm_ble_timeout"); + + switch (p_tle->event) + { + case BTU_TTYPE_BLE_INQUIRY: + btm_ble_stop_scan(); + break; + + case BTU_TTYPE_BLE_GAP_LIM_DISC: + /* lim_timeout expiried, limited discovery should exit now */ + btm_ble_update_adv_flag(BTM_BLE_NON_LIMIT_DISC_FLAG); + + btm_ble_stop_adv(); + break; + + case BTU_TTYPE_BLE_RANDOM_ADDR: + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE && + btm_cb.ble_ctr_cb.inq_var.own_addr_type == BLE_ADDR_RANDOM) + { + /* refresh the random addr */ + btm_gen_resolvable_private_addr(); + } + break; + + } +} + +/******************************************************************************* +** +** Function btm_ble_connected +** +** Description This function is when a LE connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT0 ("btm_ble_connected"); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT4 ("Security Manager: btm_sec_connected : handle:%d enc_mode:%d bda:%x RName:%s", + handle, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5], + p_dev_rec->sec_bd_name); + + BTM_TRACE_DEBUG1 ("btm_ble_connected sec_flags=0x%x",p_dev_rec->sec_flags); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: btm_sec_connected: handle:%d enc_mode:%d bda:%x ", + handle, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5]); + } +#endif + + if (!p_dev_rec) + { + /* There is no device record for new connection. Allocate one */ + p_dev_rec = btm_sec_alloc_dev (bda); + } + else /* Update the timestamp for this device */ + { + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + } + + p_dev_rec->device_type = BT_DEVICE_TYPE_BLE; + p_dev_rec->hci_handle = handle; + if (role == HCI_ROLE_MASTER) + p_dev_rec->role_master = TRUE; + + p_cb->inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + + return; +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_features_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read LE remote feature supported +** complete event. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_features_complete(UINT8 *p) +{ + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT8 size; + UINT16 handle; + int xx, yy; + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_EVENT0 ("btm_ble_read_remote_features_complete "); + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (size, p); + + /* Look up the connection by handle and copy features */ + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++) + { + if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle)) + { + for (yy = 0; yy < BD_FEATURES_LEN; yy++) + STREAM_TO_UINT8 (p_acl_cb->features[yy], p); + + p_dev_rec = btm_find_dev_by_handle (handle); + if (!p_dev_rec) + { + /* Get a new device; might be doing dedicated bonding */ + p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr); + } + + memcpy (p_dev_rec->features, p_acl_cb->features, BD_FEATURES_LEN); + break; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_write_adv_enable_complete +** +** Description This function process the write adv enable command complete. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_write_adv_enable_complete(UINT8 * p) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + /* if write adv enable/disbale not succeed */ + if (*p != HCI_SUCCESS) + { + /* toggle back the adv mode */ + p_cb->adv_mode = !p_cb->adv_mode; + } +} +/******************************************************************************* +** +** Function btm_ble_init +** +** Description Initialize the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_init (void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT0 ("btm_ble_init "); + + memset(p_cb, 0, sizeof(tBTM_BLE_CB)); + + p_cb->inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + p_cb->inq_var.adv_chnl_map = BTM_BLE_DEFAULT_ADV_CHNL_MAP; + p_cb->inq_var.afp = BTM_BLE_DEFAULT_AFP; + p_cb->inq_var.sfp = BTM_BLE_DEFAULT_SFP; + p_cb->inq_var.connectable_mode = BTM_BLE_NON_CONNECTABLE; + p_cb->inq_var.discoverable_mode = BTM_BLE_NON_DISCOVERABLE; + + /* for background connection, reset connection params to be undefined */ + p_cb->scan_int = p_cb->scan_win = BTM_BLE_CONN_PARAM_UNDEF; + + p_cb->inq_var.evt_type = BTM_BLE_UNKNOWN_EVT; +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h new file mode 100644 index 0000000..449de8f --- /dev/null +++ b/stack/btm/btm_ble_int.h @@ -0,0 +1,299 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ + +#ifndef BTM_BLE_INT_H +#define BTM_BLE_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" +#include "btm_ble_api.h" +#include "btm_int.h" + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#include "smp_api.h" +#endif + +#define BTM_BLE_CONNECT_EVT 0x00 +#define BTM_BLE_CONNECT_DIR_EVT 0x01 +#define BTM_BLE_DISCOVER_EVT 0x02 +#define BTM_BLE_NON_CONNECT_EVT 0x03 +#define BTM_BLE_SCAN_RSP_EVT 0x04 +#define BTM_BLE_SCAN_REQ_EVT 0x06 +#define BTM_BLE_UNKNOWN_EVT 0xff + +#define BTM_BLE_UNKNOWN_EVT 0xff + +/* scanning enable status */ +#define BTM_BLE_SCAN_ENABLE 0x01 +#define BTM_BLE_SCAN_DISABLE 0x00 + +/* advertising enable status */ +#define BTM_BLE_ADV_ENABLE 0x01 +#define BTM_BLE_ADV_DISABLE 0x00 + +/* use the high 4 bits unused by inquiry mode */ +#define BTM_BLE_SELECT_SCAN 0x20 +#define BTM_BLE_NAME_REQUEST 0x40 +#define BTM_BLE_OBSERVE 0x80 + +#define BTM_BLE_MAX_WL_ENTRY 1 +#define BTM_BLE_AD_DATA_LEN 31 + +#define BTM_BLE_ENC_MASK 0x03 + +#define BTM_BLE_DUPLICATE_ENABLE 1 +#define BTM_BLE_DUPLICATE_DISABLE 0 + +#define BTM_BLE_GAP_DISC_SCAN_INT 18 /* Interval(scan_int) = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_DISC_SCAN_WIN 18 /* scan_window = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_INT 512 /* Tgap(gen_disc) = 1.28 s= 512 * 0.625 ms */ +#define BTM_BLE_GAP_LIM_TOUT 30 /* Tgap(lim_timeout) = 30.72 s max, round down to 30 */ + + +#define BTM_BLE_SEC_REQ_ACT_NONE 0 +#define BTM_BLE_SEC_REQ_ACT_ENCRYPT 1 /* encrypt the link using current key or key refresh */ +#define BTM_BLE_SEC_REQ_ACT_PAIR 2 +#define BTM_BLE_SEC_REQ_ACT_DISCARD 3 /* discard the sec request while encryption is started but not completed */ +typedef UINT8 tBTM_BLE_SEC_REQ_ACT; + + + +typedef struct +{ + UINT16 data_mask; + UINT8 *p_flags; + UINT8 ad_data[BTM_BLE_AD_DATA_LEN]; + UINT8 *p_pad; +}tBTM_BLE_LOCAL_ADV_DATA; + +typedef struct +{ + UINT32 inq_count; /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + BOOLEAN scan_rsp; + tBLE_BD_ADDR le_bda; +} tINQ_LE_BDADDR; + +#define BTM_BLE_ADV_DATA_LEN_MAX 31 +#define BTM_BLE_CACHE_ADV_DATA_MAX 62 + +#define BTM_BLE_VALID_PRAM(x, min, max) (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF)) + +typedef struct +{ + + UINT16 discoverable_mode; + UINT16 connectable_mode; + UINT16 br_edr_supported_flag; /* combined BR EDR discoverable and connectable mode */ + /* only meaningful when it is zero. This means + BR EDR is not supported*/ + UINT8 proc_mode; /* current procedure mode : inquiry or discovery */ + + UINT16 scan_window; + UINT16 scan_interval; + UINT8 scan_type; /* current scan type: active or passive */ + UINT16 adv_interval_min; + UINT16 adv_interval_max; + tBLE_ADDR_TYPE own_addr_type; + tBTM_BLE_AFP afp; /* advertising filter policy */ + tBTM_BLE_SFP sfp; /* scanning filter policy */ + + UINT8 evt_type; + UINT8 adv_mode; + tBLE_BD_ADDR direct_bda; + + UINT8 adv_len; + UINT8 adv_data_cache[BTM_BLE_CACHE_ADV_DATA_MAX]; + + /* inquiry BD addr database */ + UINT8 num_bd_entries; + UINT8 max_bd_entries; + + tBLE_BD_ADDR local_bda; + + tBTM_BLE_LOCAL_ADV_DATA adv_data; + tBTM_BLE_ADV_CHNL_MAP adv_chnl_map; + + TIMER_LIST_ENT inq_timer_ent; + BOOLEAN scan_rsp; + UINT8 state; /* Current state that the inquiry process is in */ + UINT8 tx_power; +} tBTM_BLE_INQ_CB; + + +/* random address resolving complete callback */ +typedef void (tBTM_BLE_RESOLVE_CBACK) (void * match_rec, void *p); + +/* random address management control block */ +typedef struct +{ + BD_ADDR private_addr; + BD_ADDR random_bda; + BOOLEAN busy; + UINT16 index; + tBTM_BLE_RESOLVE_CBACK *p_resolve_cback; + void *p; + TIMER_LIST_ENT raddr_timer_ent; +} tBTM_LE_RANDOM_CB; + +#define BTM_BLE_MAX_BG_CONN_DEV_NUM 10 + +typedef struct +{ + UINT16 min_conn_int; + UINT16 max_conn_int; + UINT16 slave_latency; + UINT16 supervision_tout; + +}tBTM_LE_CONN_PRAMS; + +/* Define BLE Device Management control structure +*/ +typedef struct +{ + /***************************************************** + ** BLE Inquiry + *****************************************************/ + tBTM_BLE_INQ_CB inq_var; + + /* background connection procedure cb value */ + tBTM_BLE_CONN_TYPE bg_conn_type; + UINT16 scan_int; + UINT16 scan_win; + tBTM_BLE_SEL_CBACK *p_select_cback; + TIMER_LIST_ENT scan_param_idle_timer; + + UINT8 bg_conn_dev_num; + BD_ADDR bg_conn_dev_list[BTM_BLE_MAX_BG_CONN_DEV_NUM]; + +#define BLE_BG_CONN_IDLE 0 +#define BLE_BG_CONN_ACTIVE 1 +#define BLE_BG_CONN_SUSPEND 2 + + UINT8 bg_conn_state; + + /* random address management control block */ + tBTM_LE_RANDOM_CB addr_mgnt_cb; + + /* white list information */ + UINT8 num_empty_filter; /* Number of entries in white list */ + UINT8 max_filter_entries; /* Maximum number of entries that can be stored */ + BOOLEAN enabled; + BOOLEAN privacy; /* privacy enabled or disabled */ + +#ifdef BTM_BLE_PC_ADV_TEST_MODE + tBTM_BLE_SCAN_REQ_CBACK *p_scan_req_cback; +#endif + +} tBTM_BLE_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void btm_ble_timeout(TIMER_LIST_ENT *p_tle); +extern void btm_ble_process_adv_pkt (UINT8 *p); +extern void btm_ble_proc_scan_rsp_rpt (UINT8 *p); +extern tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb); +extern BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda); + +extern tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode); +extern tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode); +extern tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); + +extern void btm_ble_stop_scan(void); +extern void btm_ble_att_db_init(void); +extern void btm_ble_init (void); +extern void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role); +extern void btm_ble_read_remote_features_complete(UINT8 *p); +extern void btm_ble_stop_adv(void); +extern void btm_ble_write_adv_enable_complete(UINT8 * p); + +/* LE security function from btm_sec.c */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +extern void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act); +extern void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +extern UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); +extern tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role); +extern void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv); +extern BOOLEAN btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +extern void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable); +#endif + +/* LE device management functions */ +extern void btm_ble_reset_id( void ); + +/* security related functions */ +extern void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ); +extern BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div); +extern BOOLEAN btm_ble_check_link_type (BD_ADDR bd_addr); +extern BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types); + +extern void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback); +extern void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, BOOLEAN pass_to_application); +extern void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size); +extern UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr); + +/* white list function */ +extern BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr,tBLE_ADDR_TYPE addr_type); +extern BOOLEAN btm_update_bg_conn_list(BOOLEAN to_add, BD_ADDR bd_addr); +extern void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy); +extern void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy); +extern void btm_ble_clear_white_list (void); +extern void btm_write_bg_conn_wl(void); + +/* background connection function */ +extern void btm_ble_suspend_bg_conn(void); +extern BOOLEAN btm_ble_resume_bg_conn(tBTM_BLE_SEL_CBACK *p_sele_callback, BOOLEAN def_param); +extern void btm_ble_update_bg_state(void); +extern void btm_ble_initiate_select_conn(BD_ADDR bda); +extern BOOLEAN btm_ble_start_auto_conn(BOOLEAN start); +extern BOOLEAN btm_ble_start_select_conn(BOOLEAN start,tBTM_BLE_SEL_CBACK *p_select_cback); +extern BOOLEAN btm_ble_find_dev_in_whitelist(BD_ADDR bd_addr); +extern BOOLEAN btm_ble_renew_bg_conn_params(BOOLEAN add, BD_ADDR bd_addr); +extern void btm_ble_scan_param_idle(void); +extern UINT8 btm_ble_count_unconn_dev_in_whitelist(void); + +/* BLE address management */ +extern tBLE_ADDR_TYPE btm_ble_map_bda_to_conn_bda(BD_ADDR bda); +extern void btm_gen_resolvable_private_addr (void); +extern void btm_gen_non_resolvable_private_addr (void); +extern void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK * p_cback, void *p); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +BT_API extern void btm_ble_set_no_disc_if_pair_fail (BOOLEAN disble_disc); +BT_API extern void btm_ble_set_test_mac_value (BOOLEAN enable, UINT8 *p_test_mac_val); +BT_API extern void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/btm/btm_dev.c b/stack/btm/btm_dev.c new file mode 100644 index 0000000..04994d1 --- /dev/null +++ b/stack/btm/btm_dev.c @@ -0,0 +1,464 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Device Manager + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "bt_types.h" +#include "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" +#include "l2c_api.h" +static tBTM_SEC_DEV_REC *btm_find_oldest_dev (void); + +/******************************************************************************* +** +** Function BTM_SecAddDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** dev_class - Device Class +** bd_name - Name of the peer device. NULL if unknown. +** features - Remote device's supported features. NULL if not known +** trusted_mask - Bitwise OR of services that do not +** require authorization. (array of UINT32) +** link_key - Connection link key. NULL if unknown. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, + BD_FEATURES features, UINT32 trusted_mask[], + LINK_KEY link_key, UINT8 key_type, tBTM_IO_CAP io_cap) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec) + { + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + p_dev_rec = &btm_cb.sec_dev_rec[i]; + + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + +#if BLE_INCLUDED == TRUE + /* use default value for background connection params */ + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + break; + } + } + + if (!p_dev_rec) + return(FALSE); + } + + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + + if (dev_class) + memcpy (p_dev_rec->dev_class, dev_class, DEV_CLASS_LEN); + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + + if (features) + memcpy (p_dev_rec->features, features, sizeof (BD_FEATURES)); + else + memset (p_dev_rec->features, 0, sizeof (BD_FEATURES)); + + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + if (link_key) + { + BTM_TRACE_EVENT6 ("BTM_SecAddDevice() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], + bd_addr[3], bd_addr[4], bd_addr[5]); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + memcpy (p_dev_rec->link_key, link_key, LINK_KEY_LEN); + p_dev_rec->link_key_type = key_type; + } + +#if defined(BTIF_MIXED_MODE_INCLUDED) && (BTIF_MIXED_MODE_INCLUDED == TRUE) + p_dev_rec->sm4 = BTM_SM4_KNOWN; +#endif + + p_dev_rec->rmt_io_caps = io_cap; + + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Parameters: bd_addr - BD address of the peer +** +** Returns TRUE if removed OK, FALSE if not found or ACL link is active +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (BTM_IsAclConnectionUp(bd_addr)) + { + BTM_TRACE_WARNING0("BTM_SecDeleteDevice FAILED: Cannot Delete when connection is active"); + return(FALSE); + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + return(FALSE); + + btm_sec_free_dev (p_dev_rec); + + /* Tell controller to get rid of the link key if it has one stored */ + BTM_DeleteStoredLinkKey (bd_addr, NULL); + + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_SecReadDevName +** +** Description Looks for the device name in the security database for the +** specified BD address. +** +** Returns Pointer to the name or NULL +** +*******************************************************************************/ +char *BTM_SecReadDevName (BD_ADDR bd_addr) +{ + char *p_name = NULL; + tBTM_SEC_DEV_REC *p_srec; + + if ((p_srec = btm_find_dev(bd_addr)) != NULL) + p_name = (char *)p_srec->sec_bd_name; + + return(p_name); +} + +/******************************************************************************* +** +** Function btm_sec_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified handle +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_INQ_INFO *p_inq_info; + int i; + BTM_TRACE_EVENT0 ("btm_sec_alloc_dev"); + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + p_dev_rec = &btm_cb.sec_dev_rec[i]; + break; + } + } + + if (!p_dev_rec) + p_dev_rec = btm_find_oldest_dev(); + + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + + /* Check with the BT manager if details about remote device are known */ + /* outgoing connection */ + if ((p_inq_info = BTM_InqDbRead(bd_addr)) != NULL) + { + memcpy (p_dev_rec->dev_class, p_inq_info->results.dev_class, DEV_CLASS_LEN); + +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + +#if BTM_INQ_GET_REMOTE_NAME == TRUE + if (p_inq_info->remote_name_state == BTM_INQ_RMT_NAME_DONE) + { + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)p_inq_info->remote_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + } +#endif + } + else + { +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type = BT_DEVICE_TYPE_BREDR; /* initialize it as BR/EDR device */ + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + + if (!memcmp (bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + } + + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + + return(p_dev_rec); +} + + +/******************************************************************************* +** +** Function btm_sec_free_dev +** +** Description Mark device record as not used +** +*******************************************************************************/ +void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec) +{ + p_dev_rec->sec_flags = 0; + +#if BLE_INCLUDED == TRUE + /* Clear out any saved BLE keys */ + btm_sec_clear_ble_keys (p_dev_rec); +#endif + + +} + +/******************************************************************************* +** +** Function btm_dev_support_switch +** +** Description This function is called by the L2CAP to check if remote +** device supports role switch +** +** Parameters: bd_addr - Address of the peer device +** +** Returns TRUE if device is known and role switch is supported +** +*******************************************************************************/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 xx; + BOOLEAN feature_empty = TRUE; + +#if BTM_SCO_INCLUDED == TRUE + /* Role switch is not allowed if a SCO is up */ + if (btm_is_sco_active_by_bdaddr(bd_addr)) + return(FALSE); +#endif + p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec && HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features)) + { + if (HCI_SWITCH_SUPPORTED(p_dev_rec->features)) + { + BTM_TRACE_DEBUG0("btm_dev_support_switch return TRUE (feature found)"); + return (TRUE); + } + + /* If the feature field is all zero, we never received them */ + for (xx = 0 ; xx < BD_FEATURES_LEN ; xx++) + { + if (p_dev_rec->features[xx] != 0x00) + { + feature_empty = FALSE; /* at least one is != 0 */ + break; + } + } + + /* If we don't know peer's capabilities, assume it supports Role-switch */ + if (feature_empty) + { + BTM_TRACE_DEBUG0("btm_dev_support_switch return TRUE (feature empty)"); + return (TRUE); + } + } + + BTM_TRACE_DEBUG0("btm_dev_support_switch return FALSE"); + return(FALSE); +} + +/******************************************************************************* +** +** Function btm_find_dev_by_handle +** +** Description Look for the record in the device database for the record +** with specified handle +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->hci_handle == handle)) + return(p_dev_rec); + } + return(NULL); +} + +/******************************************************************************* +** +** Function btm_find_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + if (bd_addr) + { + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (!memcmp (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN))) + return(p_dev_rec); + } + } + return(NULL); +} + +/******************************************************************************* +** +** Function btm_find_or_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_EVENT0 ("btm_find_or_alloc_dev"); + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_sec_alloc_dev (bd_addr); + } + return(p_dev_rec); +} + +/******************************************************************************* +** +** Function btm_find_oldest_dev +** +** Description Locates the oldest device in use. It first looks for +** the oldest non-paired device. If all devices are paired it +** deletes the oldest paired device. +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_oldest_dev (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + tBTM_SEC_DEV_REC *p_oldest = p_dev_rec; + UINT32 ot = 0xFFFFFFFF; + int i; + + /* First look for the non-paired devices for the oldest entry */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if (((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + || ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0)) + continue; /* Device is paired so skip it */ + + if (p_dev_rec->timestamp < ot) + { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + + if (ot != 0xFFFFFFFF) + return(p_oldest); + + /* All devices are paired; find the oldest */ + p_dev_rec = &btm_cb.sec_dev_rec[0]; + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + continue; + + if (p_dev_rec->timestamp < ot) + { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + return(p_oldest); +} + + diff --git a/stack/btm/btm_devctl.c b/stack/btm/btm_devctl.c new file mode 100644 index 0000000..f0fc437 --- /dev/null +++ b/stack/btm/btm_devctl.c @@ -0,0 +1,1953 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle BTM interface functions for the + * Bluetooth device including Rest, HCI buffer size and others + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" + +#if BLE_INCLUDED == TRUE +#include "gatt_int.h" + +#if GAP_INCLUDED == TRUE +#include "gap_api.h" +#include "gattdefs.h" +#endif + +#endif /* BLE_INCLUDED */ + +/* BTM_APP_DEV_INIT should be defined if additional controller initialization is +** needed by the application to be performed after the HCI reset +*/ +#ifdef BTM_APP_DEV_INIT +extern void BTM_APP_DEV_INIT(void); +#endif + +#ifdef BTA_PRM_CHECK_FW_VER +extern BOOLEAN BTA_PRM_CHECK_FW_VER(UINT8 *p); +#endif + +#ifndef TT_DEV_RESET_MASK +#define TT_DEV_RESET_MASK 0xff +#endif + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +/* The default class of device. */ +#ifndef BTM_INIT_CLASS_OF_DEVICE +#define BTM_INIT_CLASS_OF_DEVICE "\x00\x1F\x00" +#endif + +#ifndef BTM_DEV_RESET_TIMEOUT +#define BTM_DEV_RESET_TIMEOUT 4 +#endif + +#define BTM_DEV_REPLY_TIMEOUT 2 /* 1 second expiration time is not good. Timer may start between 0 and 1 second. */ + /* if it starts at the very end of the 0 second, timer will expire really easily. */ + +#define BTM_INFO_TIMEOUT 5 /* 5 seconds for info response */ + +/* After Reset a timeout can be specified in the target.h for specific targets + * that may require additional time to reset + * otherwise no timeout is required +*/ +#ifndef BTM_AFTER_RESET_TIMEOUT +#define BTM_AFTER_RESET_TIMEOUT 0 +#endif + +/* Internal baseband so the parameters such as local features, version etc. are known +so there is no need to issue HCI commands and wait for responses at BTM initialization */ +#ifndef BTM_INTERNAL_BB +#define BTM_INTERNAL_BB FALSE +#endif + +/* The local version information in the format specified in the HCI read local version +response message */ +#ifndef BTM_INTERNAL_LOCAL_VER +#define BTM_INTERNAL_LOCAL_VER {0x00, 0x01, 0x05, 0x81, 0x01, 0x30, 0x00, 0x40, 0x8D} +#endif + +/* The local features information in the format specified in the HCI read local features +response message */ +#ifndef BTM_INTERNAL_LOCAL_FEA +#define BTM_INTERNAL_LOCAL_FEA {0x00, 0xFF, 0xF9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#endif + +#ifndef BTM_SET_DEV_NAME_UPON_RESET +#define BTM_SET_DEV_NAME_UPON_RESET TRUE +#endif + +/* host SCO buffer size */ +#ifndef BTM_SCO_HOST_BUF_SIZE +#define BTM_SCO_HOST_BUF_SIZE 0xff +#endif + +#ifndef BTM_GPS_UIPC_CH_NB +#define BTM_GPS_UIPC_CH_NB UIPC_CH_ID_1 +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void btm_dev_reset (void); +static void btm_after_reset_hold_complete (void); +static void btm_continue_reset (void); + +/******************************************************************************* +** +** Function btm_dev_init +** +** Description This function is on the BTM startup +** +** Returns void +** +*******************************************************************************/ +void btm_dev_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.devcb, 0, sizeof (tBTM_DEVCB)); +#endif + + /* Initialize nonzero defaults */ +#if (BTM_MAX_LOC_BD_NAME_LEN > 0) + memset(btm_cb.cfg.bd_name, 0, sizeof(tBTM_LOC_BD_NAME)); +#if (BTM_USE_DEF_LOCAL_NAME == TRUE) + BCM_STRNCPY_S(btm_cb.cfg.bd_name, sizeof(btm_cb.cfg.bd_name), BTM_DEF_LOCAL_NAME, BTM_MAX_LOC_BD_NAME_LEN); +#endif +#endif + + btm_cb.devcb.reset_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RESET; + btm_cb.devcb.rln_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RLN; + btm_cb.devcb.rlinkp_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RLNKP; + + btm_cb.btm_acl_pkt_types_supported = BTM_ACL_PKT_TYPES_MASK_DH1 + BTM_ACL_PKT_TYPES_MASK_DM1 + + BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3 + + BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5; + + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1 + + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5; + + btm_cb.first_disabled_channel = 0xff; /* To allow disabling 0th channel alone */ + btm_cb.last_disabled_channel = 0xff; /* To allow disabling 0th channel alone */ + +#if (BTM_AUTOMATIC_HCI_RESET == TRUE) + +#if (BTM_FIRST_RESET_DELAY > 0) + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_FIRST_RESET_DELAY); +#else + btm_dev_reset(); +#endif + +#else + BTM_TRACE_EVENT0 ("BTM_AUTOMATIC_HCI_RESET is FALSE, so skip btm reset for now"); +#endif + +} + + +/******************************************************************************* +** +** Function btm_db_reset +** +** Description This function is called by BTM_DeviceReset and clears out any +** pending callbacks for inquiries, discoveries, other pending +** functions that may be in progress. +** +** Returns void +** +*******************************************************************************/ +static void btm_db_reset (void) +{ + tBTM_CMPL_CB *p_cb; + tBTM_STATUS status = BTM_DEV_RESET; + + btm_inq_db_reset(); + + if (btm_cb.devcb.p_rln_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rln_cmpl_cb; + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) NULL); + } + + if (btm_cb.devcb.p_rlinkp_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb; + btm_cb.devcb.p_rlinkp_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) &status); + } + + if (btm_cb.devcb.p_rssi_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((tBTM_RSSI_RESULTS *) &status); + } +} + + + +/******************************************************************************* +** +** Function btm_dev_absent +** +** Description This function is called by when it is detected that the +** device is not connected any more. +** +** Returns void +** +*******************************************************************************/ +void btm_dev_absent (void) +{ + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + + btm_db_reset (); + btm_inq_db_reset(); + + /* If anyone wants device status notifications, give him one */ + btm_report_device_status (BTM_DEV_STATUS_DOWN); + + btu_stop_timer (&btm_cb.devcb.reset_timer); +} + + +/******************************************************************************* +** +** Function BTM_DeviceReset +** +** Description This function is called to reset the HCI. Callback function +** if provided is called when startup of the device is +** completed. +** +** Returns void +** +*******************************************************************************/ +void BTM_DeviceReset (tBTM_CMPL_CB *p_cb) +{ + tBTM_STATUS status; + + /* If device is already resetting, do not allow another */ + if ((!btm_cb.devcb.p_reset_cmpl_cb) || (btm_cb.devcb.p_reset_cmpl_cb == p_cb)) + { + /* Flush all ACL connections */ + btm_acl_device_down(); + + /* Clear the callback, so application would not hang on reset */ + btm_db_reset(); + + /* Save address of the completion routine, if provided */ + btm_cb.devcb.p_reset_cmpl_cb = p_cb; + + btm_dev_reset (); + } + else + { + /* pass an error to the bad callback, another one was already provided */ + if (p_cb) + { + status = BTM_ILLEGAL_VALUE; + p_cb (&status); + } + } +} + + +/******************************************************************************* +** +** Function BTM_IsDeviceUp +** +** Description This function is called to check if the device is up. +** +** Returns TRUE if device is up, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_IsDeviceUp (void) +{ + return ((BOOLEAN) (btm_cb.devcb.state == BTM_DEV_STATE_READY)); +} + +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called disable channels +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannels (UINT8 first, UINT8 last) +{ + BTM_TRACE_API4 ("BTM_SetAfhChannels first: %d (%d) last: %d (%d)", + first, btm_cb.first_disabled_channel, last, + btm_cb.last_disabled_channel); + + /* Make sure the local device supports the feature before sending */ + if ((!HCI_LMP_AFH_CAP_MASTR_SUPPORTED(btm_cb.devcb.local_features)) && + (!HCI_LMP_AFH_CLASS_SLAVE_SUPPORTED(btm_cb.devcb.local_features)) && + (!HCI_LMP_AFH_CLASS_MASTR_SUPPORTED(btm_cb.devcb.local_features))) + return (BTM_MODE_UNSUPPORTED); + + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if ((btm_cb.first_disabled_channel != first) + || (btm_cb.last_disabled_channel != last)) + { + if (btsnd_hcic_set_afh_channels (first, last)) + { + btm_cb.first_disabled_channel = first; + btm_cb.last_disabled_channel = last; + } + else + return (BTM_NO_RESOURCES); + } + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetAfhChannelAssessment +** +** Description This function is called to set the channel assessment mode on or off +** +** Returns none +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannelAssessment (BOOLEAN enable_or_disable) +{ + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_AFH_CAP_SLAVE_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + if (!btsnd_hcic_write_afh_channel_assessment_mode (enable_or_disable)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ContinueReset +** +** Description This function is called by the application to continue +** initialization after the application has completed its +** vendor specific sequence. It is only used when +** BTM_APP_DEV_INIT is defined in target.h. +** +** Returns void +** +*******************************************************************************/ +void BTM_ContinueReset (void) +{ +#ifdef BTM_APP_DEV_INIT + btm_continue_reset(); +#endif +} + +/******************************************************************************* +** +** Function btm_dev_reset +** +** Description Local function called to send a reset command +** +** Returns void +** +*******************************************************************************/ +static void btm_dev_reset (void) +{ + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + + /* flush out the command complete queue and command transmit queue */ + btu_hcif_flush_cmd_queue(); + + /* Start reset timer. When timer expires we will send first command */ + /* from the setup sequence */ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, + BTM_DEV_RESET_TIMEOUT); + btsnd_hcic_reset (LOCAL_BR_EDR_CONTROLLER_ID); +} + + +/******************************************************************************* +** +** Function btm_get_hci_buf_size +** +** Description Local function called to send a read buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_get_hci_buf_size (void) +{ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_read_buffer_size (); + +} +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_read_ble_wl_size +** +** Description Local function called to send a read BLE buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_read_ble_wl_size(void) +{ + BTM_TRACE_DEBUG0("btm_read_ble_wl_size "); + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_ble_read_white_list_size(); +} +/******************************************************************************* +** +** Function btm_get_ble_buffer_size +** +** Description Local function called to send a read BLE buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_get_ble_buffer_size(void) +{ + BTM_TRACE_DEBUG0("btm_get_ble_buffer_size "); + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_ble_read_buffer_size (); +} +#endif +/******************************************************************************* +** +** Function btm_get_local_version +** +** Description Local function called to send a read local version to controller +** +** Returns void +** +*******************************************************************************/ +void btm_get_local_version (void) +{ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Local Version message to the Host Controller. */ + btsnd_hcic_read_local_ver (LOCAL_BR_EDR_CONTROLLER_ID); + btsnd_hcic_read_bd_addr (); + +#if BTM_PWR_MGR_INCLUDED == TRUE + btm_pm_reset(); +#endif + +} + +/******************************************************************************* +** +** Function btm_get_local_features +** +** Description Local function called to send a read local features +** +** Returns void +** +*******************************************************************************/ +void btm_get_local_features (void) +{ + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + btsnd_hcic_read_local_features (); +} + +/******************************************************************************* +** +** Function btm_dev_timeout +** +** Description This function is called when a timer list entry expires. +** +** Returns void +** +*******************************************************************************/ +void btm_dev_timeout (TIMER_LIST_ENT *p_tle) +{ + TIMER_PARAM_TYPE timer_type = (TIMER_PARAM_TYPE)p_tle->param; + + if ((timer_type & TT_DEV_RESET_MASK) == TT_DEV_RESET) + { + /* Call device reset as long as there is timeout*/ + btm_dev_reset(); + } + else if (timer_type == (TIMER_PARAM_TYPE)TT_DEV_RLN) + { + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) NULL); + } +} + +/******************************************************************************* +** +** Function btm_reset_complete +** +** Description This function is called when command complete for HCI_Reset +** is received. It does not make sense to send next command +** because device is resetting after command complete is +** received. Just start timer and set required state. +** +** Returns void +** +*******************************************************************************/ +void btm_reset_complete (void) +{ + int devinx; + + BTM_TRACE_EVENT0 ("btm_reset_complete"); + +#ifdef BRCM_VS + btm_vs_reset_complete(); +#endif + + + /* Handle if btm initiated the reset */ + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT) + { + /* Tell L2CAP that all connections are gone */ + l2cu_device_reset (); + + /* Clear current security state */ + for (devinx = 0; devinx < BTM_SEC_MAX_DEVICE_RECORDS; devinx++) + { + btm_cb.sec_dev_rec[devinx].sec_state = BTM_SEC_STATE_IDLE; + } + + /* After the reset controller should restore all parameters to defaults. */ + btm_cb.btm_inq_vars.inq_counter = 1; + btm_cb.btm_inq_vars.inq_scan_window = HCI_DEF_INQUIRYSCAN_WINDOW; + btm_cb.btm_inq_vars.inq_scan_period = HCI_DEF_INQUIRYSCAN_INTERVAL; + btm_cb.btm_inq_vars.inq_scan_type = HCI_DEF_SCAN_TYPE; + + btm_cb.btm_inq_vars.page_scan_window = HCI_DEF_PAGESCAN_WINDOW; + btm_cb.btm_inq_vars.page_scan_period = HCI_DEF_PAGESCAN_INTERVAL; + btm_cb.btm_inq_vars.page_scan_type = HCI_DEF_SCAN_TYPE; + +#if (BTM_AFTER_RESET_TIMEOUT > 0) + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, + BTM_AFTER_RESET_TIMEOUT); +#else + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_AFTER_RESET; + btm_after_reset_hold_complete(); +#endif + +#if (BLE_INCLUDED == TRUE) + btm_cb.ble_ctr_cb.bg_conn_state = BLE_BG_CONN_IDLE; + btm_cb.ble_ctr_cb.bg_conn_dev_num = 0; + btm_cb.ble_ctr_cb.bg_conn_type = BTM_BLE_CONN_NONE; + btm_cb.ble_ctr_cb.p_select_cback = NULL; + memset(&btm_cb.ble_ctr_cb.bg_conn_dev_list, 0, (sizeof(BD_ADDR)*BTM_BLE_MAX_BG_CONN_DEV_NUM)); + gatt_reset_bgdev_list(); +#endif + } +} + +/******************************************************************************* +** +** Function btm_continue_reset +** +** Description This function is called when wait period expired after +** device reset or called by the application to continue +** initialization after the application has completed its +** vendor specific sequence. +** +** Returns void +** +*******************************************************************************/ +void btm_continue_reset (void) +{ + + /* Reinitialize the default class of device */ +#if BTM_INTERNAL_BB == TRUE + btsnd_hcic_read_bd_addr (); +#if BTM_PWR_MGR_INCLUDED == TRUE + btm_pm_reset(); +#endif +#endif + + btm_get_hci_buf_size (); + + /* default device class */ + BTM_SetDeviceClass((UINT8 *) BTM_INIT_CLASS_OF_DEVICE); + +#if (BTM_MAX_LOC_BD_NAME_LEN > 0) && (BTM_SET_DEV_NAME_UPON_RESET == TRUE) + BTM_SetLocalDeviceName(btm_cb.cfg.bd_name); +#elif BTM_USE_DEF_LOCAL_NAME == TRUE + BTM_SetLocalDeviceName(BTM_DEF_LOCAL_NAME); +#endif + + BTM_SetPinType (btm_cb.cfg.pin_type, btm_cb.cfg.pin_code, btm_cb.cfg.pin_code_len); +} + +/******************************************************************************* +** +** Function btm_after_reset_hold_complete +** +** Description This function is called when wait period expired after +** device reset. Continue intitialization +** +** Returns void +** +*******************************************************************************/ +void btm_after_reset_hold_complete (void) +{ +#ifdef BTM_APP_DEV_INIT + btu_stop_timer(&btm_cb.devcb.reset_timer); + BTM_APP_DEV_INIT(); +#else + btm_continue_reset(); +#endif +} + + +/******************************************************************************* +** +** Function btm_read_hci_buf_size_complete +** +** Description This function is called when command complete for +** get HCI buffer size is received. Start timer and send +** read local featues request +** +** Returns void +** +*******************************************************************************/ +void btm_read_hci_buf_size_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT8 lm_sco_buf_size; + UINT16 lm_num_acl_bufs; + UINT16 lm_num_sco_bufs; + UINT16 acl_buf_size; + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (btu_cb.hcit_acl_data_size, p); + STREAM_TO_UINT8 (lm_sco_buf_size, p); + STREAM_TO_UINT16 (lm_num_acl_bufs, p); + STREAM_TO_UINT16 (lm_num_sco_bufs, p); + + btu_cb.hcit_acl_pkt_size = btu_cb.hcit_acl_data_size + HCI_DATA_PREAMBLE_SIZE; + + l2c_link_processs_num_bufs (lm_num_acl_bufs); + +#if BTM_ACL_BUF_SIZE > 0 + acl_buf_size = (BTM_ACL_BUF_SIZE < L2CAP_MTU_SIZE) ? BTM_ACL_BUF_SIZE : L2CAP_MTU_SIZE; +#else + acl_buf_size = L2CAP_MTU_SIZE; +#endif + /* Tell the controller what our buffer sizes are. ?? Need SCO info */ + btsnd_hcic_set_host_buf_size (acl_buf_size, BTM_SCO_HOST_BUF_SIZE, L2CAP_HOST_FC_ACL_BUFS, 10); + +#if L2CAP_HOST_FLOW_CTRL == TRUE + btsnd_hcic_set_host_flow_ctrl (HCI_HOST_FLOW_CTRL_ACL_ON); +#endif + } + + /* Set the device into connectable and/or discoverable mode (if configured to do so) */ +#if BTM_IS_CONNECTABLE == TRUE + (void) BTM_SetConnectability (BTM_CONNECTABLE, BTM_DEFAULT_CONN_WINDOW, BTM_DEFAULT_CONN_INTERVAL); +#endif + +#if BTM_IS_DISCOVERABLE == TRUE + (void) BTM_SetDiscoverability (BTM_DEFAULT_DISC_MODE, BTM_DEFAULT_DISC_WINDOW, BTM_DEFAULT_DISC_INTERVAL); +#endif + +#if BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_VER; + btm_read_local_version_complete( buf, 9 ); + } +#else + btm_get_local_version (); +#endif +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_read_ble_buf_size_complete +** +** Description This function is called when command complete for +** get HCI buffer size is received. Start timer and send +** read local featues request +** +** Returns void +** +*******************************************************************************/ +void btm_read_ble_buf_size_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 lm_num_le_bufs; + + BTM_TRACE_DEBUG0("btm_read_ble_buf_size_complete "); + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (btu_cb.hcit_ble_acl_data_size, p); + STREAM_TO_UINT8 (lm_num_le_bufs, p); + + btu_cb.hcit_ble_acl_pkt_size = btu_cb.hcit_ble_acl_data_size + HCI_DATA_PREAMBLE_SIZE; + + l2c_link_processs_ble_num_bufs (lm_num_le_bufs); + } + +#if BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_FEA; + btm_read_local_features_complete( buf, 9 ); + } +#else +#ifdef BRCM_VS + btm_brcm_feat_init(); +#else + /* get local feature if BRCM specific feature is not included */ + btm_get_local_features (); +#endif +#endif + +} + +/******************************************************************************* +** +** Function btm_read_white_list_size_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + BTM_TRACE_DEBUG0("btm_read_white_list_size_complete "); + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.max_filter_entries, p); + btm_cb.ble_ctr_cb.num_empty_filter = btm_cb.ble_ctr_cb.max_filter_entries; + } + + btm_get_ble_buffer_size(); +} + +#endif +/******************************************************************************* +** +** Function btm_read_local_version_complete +** +** Description This function is called when local BD Addr read complete +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_version_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_VERSION_INFO *p_vi = &btm_cb.devcb.local_version; + UINT8 status; + +#ifdef BTA_PRM_CHECK_FW_VER + if(BTA_PRM_CHECK_FW_VER(p)) + return; +#endif + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + + STREAM_TO_UINT8 (p_vi->hci_version, p); + STREAM_TO_UINT16 (p_vi->hci_revision, p); + STREAM_TO_UINT8 (p_vi->lmp_version, p); + STREAM_TO_UINT16 (p_vi->manufacturer, p); + STREAM_TO_UINT16 (p_vi->lmp_subversion, p); + } + +#if BLE_INCLUDED == TRUE + { + btm_read_ble_wl_size(); + } +#elif BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_FEA; + btm_read_local_features_complete( buf, 9 ); + } +#else +#ifdef BRCM_VS + btm_brcm_feat_init(); +#else + /* get local feature if BRCM specific feature is not included */ + btm_get_local_features (); +#endif +#endif +} + + +/******************************************************************************* +** +** Function btm_read_local_features_complete +** +** Description This function is called when local features read is complete. +** This is the last step of the startup sequence. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_features_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + tBTM_CMPL_CB *p_cb = p_devcb->p_reset_cmpl_cb; + UINT8 status; + UINT16 xx; + UINT8 last; + UINT8 first; + + btu_stop_timer (&btm_cb.devcb.reset_timer); + /* If there was a callback address for reset complete, call it */ + p_devcb->p_reset_cmpl_cb = NULL; + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + /* stop guard timer to avoid accidental timeout */ + btu_stop_timer(&p_devcb->reset_timer); + + p_devcb->state = BTM_DEV_STATE_READY; + + /* Extract features and create "btm_acl_pkt_types_supported" flag + */ + for (xx = 0; xx < HCI_NUM_FEATURE_BYTES; xx++) + STREAM_TO_UINT8 (p_devcb->local_features[xx], p); + + /* Create ACL supported packet types mask + */ + btm_cb.btm_acl_pkt_types_supported = (BTM_ACL_PKT_TYPES_MASK_DH1 + + BTM_ACL_PKT_TYPES_MASK_DM1); + + if (HCI_3_SLOT_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH3 + + BTM_ACL_PKT_TYPES_MASK_DM3); + + if (HCI_5_SLOT_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH5 + + BTM_ACL_PKT_TYPES_MASK_DM5); + + /* _NO_X_DXX masks are reserved before ver 2.0. + Set them only for later versions of controller */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + + /* Add in EDR related ACL types */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p_devcb->local_features)) + { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_2_DH5); + } + + if (!HCI_EDR_ACL_3MPS_SUPPORTED(p_devcb->local_features)) + { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + + /* Check to see if 3 and 5 slot packets are available */ + if (HCI_EDR_ACL_2MPS_SUPPORTED(p_devcb->local_features) || + HCI_EDR_ACL_3MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3); + + if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + } + + BTM_TRACE_DEBUG1("Local supported ACL packet types: 0x%04x", + btm_cb.btm_acl_pkt_types_supported); + + /* Create (e)SCO supported packet types mask + */ + btm_cb.btm_sco_pkt_types_supported = 0; +#if BTM_SCO_INCLUDED == TRUE + btm_cb.sco_cb.esco_supported = FALSE; +#endif + if (HCI_SCO_LINK_SUPPORTED(p_devcb->local_features)) + { + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1; + + if (HCI_HV2_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV2; + + if (HCI_HV3_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV3; + } + + if (HCI_ESCO_EV3_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV3; + + if (HCI_ESCO_EV4_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV4; + + if (HCI_ESCO_EV5_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV5; +#if BTM_SCO_INCLUDED == TRUE + if (btm_cb.btm_sco_pkt_types_supported & BTM_ESCO_LINK_ONLY_MASK) + { + btm_cb.sco_cb.esco_supported = TRUE; + + /* Add in EDR related eSCO types */ + if (HCI_EDR_ESCO_2MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_2_EV5; + } + else + { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5); + } + + if (HCI_EDR_ESCO_3MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_3_EV5; + } + else + { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + } +#endif + + BTM_TRACE_DEBUG1("Local supported SCO packet types: 0x%04x", + btm_cb.btm_sco_pkt_types_supported); + + /* Create Default Policy Settings + */ + if (HCI_SWITCH_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + + if (HCI_HOLD_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_HOLD_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_HOLD_MODE; + + if (HCI_SNIFF_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_SNIFF_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_SNIFF_MODE; + + if (HCI_PARK_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_PARK_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_PARK_MODE; + + btm_sec_dev_reset (); + + /* If 802.11 present might have to disable some channels */ + if (btm_cb.last_disabled_channel != 0xff) + { + last = btm_cb.last_disabled_channel; + first = btm_cb.first_disabled_channel; + btm_cb.last_disabled_channel = 0xff; + btm_cb.first_disabled_channel = 0xff; + BTM_SetAfhChannels(first, last); + } + +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + if (HCI_LMP_INQ_RSSI_SUPPORTED(p_devcb->local_features)) + { + if (HCI_EXT_INQ_RSP_SUPPORTED(p_devcb->local_features)) + BTM_SetInquiryMode (BTM_INQ_RESULT_EXTENDED); + else + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); + } +#else + if (HCI_LMP_INQ_RSSI_SUPPORTED(p_devcb->local_features)) + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); +#endif +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if( HCI_NON_FLUSHABLE_PB_SUPPORTED(p_devcb->local_features)) + l2cu_set_non_flushable_pbf(TRUE); + else + l2cu_set_non_flushable_pbf(FALSE); +#endif + BTM_SetPageScanType (BTM_DEFAULT_SCAN_TYPE); + BTM_SetInquiryScanType (BTM_DEFAULT_SCAN_TYPE); + + /* If anyone wants device status notifications, give him one */ + btm_report_device_status (BTM_DEV_STATUS_UP); + +#ifdef BRCM_VS + btm_brcm_arc_init(); +#endif + + /* Reset sequence is complete. If this was an application originated */ + /* reset, tell him its done. */ + if (p_cb) + (*p_cb)((void *) NULL); + } +} + +/******************************************************************************* +** +** Function btm_get_voice_coding_support +** +** Description This function is provides a way to get the voice coding schemes +** supported the device. +** +** Returns A bit mask - Bit 0 if set indicates CVSD support +** Bit 1 if set indicates PCM A-law support +** Bit 2 if set indicates PCM Mu-law support +** +*******************************************************************************/ + +UINT8 btm_get_voice_coding_support( void ) +{ + UINT8 code = 0; + + if( HCI_LMP_CVSD_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x01 ; + if( HCI_LMP_A_LAW_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x02 ; + if( HCI_LMP_U_LAW_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x04 ; + + return code ; +} + +/******************************************************************************* +** +** Function BTM_SetLocalDeviceName +** +** Description This function is called to set the local device name. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLocalDeviceName (char *p_name) +{ + UINT8 *p; +#if BLE_INCLUDED == TRUE && GAP_INCLUDED == TRUE + tGAP_BLE_ATTR_VALUE attr_value; +#endif + + if (!p_name || !p_name[0] || (strlen ((char *)p_name) > BD_NAME_LEN)) + return (BTM_ILLEGAL_VALUE); + + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT || + btm_cb.devcb.state == BTM_DEV_STATE_WAIT_AFTER_RESET) + return (BTM_DEV_RESET); + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + /* Save the device name if local storage is enabled */ + p = (UINT8 *)btm_cb.cfg.bd_name; + if (p != (UINT8 *)p_name) + { + BCM_STRNCPY_S(btm_cb.cfg.bd_name, sizeof(btm_cb.cfg.bd_name), p_name, BTM_MAX_LOC_BD_NAME_LEN); + btm_cb.cfg.bd_name[BTM_MAX_LOC_BD_NAME_LEN] = '\0'; + } +#else + p = (UINT8 *)p_name; +#endif + +#if BLE_INCLUDED == TRUE && GAP_INCLUDED == TRUE + attr_value.p_dev_name = (UINT8 *)p_name; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_DEVICE_NAME, &attr_value); +#endif + + if (btsnd_hcic_change_name(p)) + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); +} + + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceName +** +** Description This function is called to read the local device name. +** +** Returns status of the operation +** If success, BTM_SUCCESS is returned and p_name points stored +** local device name +** If BTM doesn't store local device name, BTM_NO_RESOURCES is +** is returned and p_name is set to NULL +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceName (char **p_name) +{ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + *p_name = btm_cb.cfg.bd_name; + return(BTM_SUCCESS); +#else + *p_name = NULL; + return(BTM_NO_RESOURCES); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceNameFromController +** +** Description Get local device name from controller. Do not use cached +** name (used to get chip-id prior to btm reset complete). +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceNameFromController (tBTM_CMPL_CB *p_rln_cmpl_cback) +{ + /* Check if rln already in progress */ + if (btm_cb.devcb.p_rln_cmpl_cb) + return(BTM_NO_RESOURCES); + + /* Save callback */ + btm_cb.devcb.p_rln_cmpl_cb = p_rln_cmpl_cback; + + btsnd_hcic_read_name(); + btu_start_timer (&btm_cb.devcb.rln_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_read_local_name_complete +** +** Description This function is called when local name read complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + UINT8 status; + + btu_stop_timer (&btm_cb.devcb.rln_timer); + + /* If there was a callback address for read local name, call it */ + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + (*p_cb)(p); + else + (*p_cb)(NULL); + } +} + + +/******************************************************************************* +** +** Function BTM_GetLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns void +** the local device address is copied into bd_addr +** +*******************************************************************************/ +void BTM_GetLocalDeviceAddr (BD_ADDR bd_addr) +{ + memcpy (bd_addr, btm_cb.devcb.local_addr, BD_ADDR_LEN); +} + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceAddr (tBTM_CMPL_CB *p_cb) +{ + if(p_cb) + (*p_cb)(btm_cb.devcb.local_addr); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_read_local_addr_complete +** +** Description This function is called when local BD Addr read complete +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_addr_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + { + STREAM_TO_BDADDR (btm_cb.devcb.local_addr, p); + } +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalVersion +** +** Description This function is called to read the local device version +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalVersion (tBTM_VERSION_INFO *p_vers) +{ + /* Make sure the device has retrieved the info (not being reset) */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + *p_vers = btm_cb.devcb.local_version; + + return (BTM_SUCCESS); +} + + + + +/******************************************************************************* +** +** Function BTM_SetDeviceClass +** +** Description This function is called to set the local device class +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDeviceClass (DEV_CLASS dev_class) +{ + if(!memcmp (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN)) + return(BTM_SUCCESS); + + memcpy (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN); + + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT || + btm_cb.devcb.state == BTM_DEV_STATE_WAIT_AFTER_RESET) + return (BTM_DEV_RESET); + + if (!btsnd_hcic_write_dev_class (dev_class)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_ReadDeviceClass +** +** Description This function is called to read the local device class +** +** Returns pointer to the device class +** +*******************************************************************************/ +UINT8 *BTM_ReadDeviceClass (void) +{ + return ((UINT8 *)btm_cb.devcb.dev_class); +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalFeatures +** +** Description This function is called to read the local features +** +** Returns pointer to the local features string +** +*******************************************************************************/ +UINT8 *BTM_ReadLocalFeatures (void) +{ + return (btm_cb.devcb.local_features); +} + +/******************************************************************************* +** +** Function BTM_ReadBrcmFeatures +** +** Description This function is called to read the Broadcom specific features +** +** Returns pointer to the Broadcom features string +** +*******************************************************************************/ +UINT8 *BTM_ReadBrcmFeatures (void) +{ + return (btm_cb.devcb.brcm_features); +} + +/******************************************************************************* +** +** Function BTM_RegisterForDeviceStatusNotif +** +** Description This function is called to register for device status +** change notifications. +** +** If one registration is already there calling function should +** save the pointer to the function that is return and +** call it when processing of the event is complete +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_DEV_STATUS_CB *BTM_RegisterForDeviceStatusNotif (tBTM_DEV_STATUS_CB *p_cb) +{ + tBTM_DEV_STATUS_CB *p_prev = btm_cb.devcb.p_dev_status_cb; + + btm_cb.devcb.p_dev_status_cb = p_cb; + return (p_prev); +} + +/******************************************************************************* +** +** Function BTM_VendorSpecificCommand +** +** Description Send a vendor specific HCI command to the controller. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_CMD_STARTED Command sent. Waiting for command cmpl event. +** +** Notes +** Opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC. +** +*******************************************************************************/ +tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, UINT8 param_len, + UINT8 *p_param_buf, tBTM_VSC_CMPL_CB *p_cb) +{ + void *p_buf; + + BTM_TRACE_EVENT2 ("BTM: BTM_VendorSpecificCommand: Opcode: 0x%04X, ParamLen: %i.", + opcode, param_len); + + /* Allocate a buffer to hold HCI command plus the callback function */ + if ((p_buf = GKI_getbuf((UINT16)(sizeof(BT_HDR) + sizeof (tBTM_CMPL_CB *) + + param_len + HCIC_PREAMBLE_SIZE))) != NULL) + { + /* Send the HCI command (opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC) */ + btsnd_hcic_vendor_spec_cmd (p_buf, opcode, param_len, p_param_buf, (void *)p_cb); + + /* Return value */ + if (p_cb != NULL) + return (BTM_CMD_STARTED); + else + return (BTM_SUCCESS); + } + else + return (BTM_NO_RESOURCES); + +} + + +/******************************************************************************* +** +** Function btm_vsc_complete +** +** Description This function is called when local HCI Vendor Specific +** Command complete message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_vsc_complete (UINT8 *p, UINT16 opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback) +{ + tBTM_VSC_CMPL vcs_cplt_params; + + /* If there was a callback address for vcs complete, call it */ + if (p_vsc_cplt_cback) + { + /* Pass paramters to the callback function */ + vcs_cplt_params.opcode = opcode; /* Number of bytes in return info */ + vcs_cplt_params.param_len = evt_len; /* Number of bytes in return info */ + vcs_cplt_params.p_param_buf = p; + (*p_vsc_cplt_cback)(&vcs_cplt_params); /* Call the VSC complete callback function */ + } +} + +/******************************************************************************* +** +** Function BTM_RegisterForVSEvents +** +** Description This function is called to register/deregister for vendor +** specific HCI events. +** +** If is_register=TRUE, then the function will be registered; +** if is_register=FALSE, then the function will be deregistered. +** +** Returns BTM_SUCCESS if successful, +** BTM_BUSY if maximum number of callbacks have already been +** registered. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegisterForVSEvents (tBTM_VS_EVT_CB *p_cb, BOOLEAN is_register) +{ + tBTM_STATUS retval = BTM_SUCCESS; + UINT8 i, free_idx = BTM_MAX_VSE_CALLBACKS; + + /* See if callback is already registered */ + for (i=0; i HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD) + num_keys = HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD; + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (btsnd_hcic_write_stored_key (num_keys, bd_addr, link_key)) + return (BTM_SUCCESS); + else + return (BTM_NO_RESOURCES); + +} + + +/******************************************************************************* +** +** Function BTM_DeleteStoredLinkKey +** +** Description This function is called to delete link key for the specified +** device addresses from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Addresses of the devices +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb) +{ + BD_ADDR local_bd_addr; + BOOLEAN delete_all_flag = FALSE; + + /* Check if the previous command is completed */ + if (btm_cb.devcb.p_stored_link_key_cmpl_cb) + return (BTM_BUSY); + + if (!bd_addr) + { + /* This is to delete all link keys */ + delete_all_flag = TRUE; + + /* We don't care the BD address. Just pass a non zero pointer */ + bd_addr = local_bd_addr; + } + + BTM_TRACE_EVENT1 ("BTM: BTM_DeleteStoredLinkKey: delete_all_flag: %s", + delete_all_flag ? "TRUE" : "FALSE"); + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (!btsnd_hcic_delete_stored_key (bd_addr, delete_all_flag)) + { + return (BTM_NO_RESOURCES); + } + else + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_read_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_read_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_READ_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_READ_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event if status is success */ + STREAM_TO_UINT8 (result.status, p); + if (result.status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (result.max_keys, p); + STREAM_TO_UINT16 (result.read_keys, p); + } + else + { + BTM_TRACE_WARNING1("Read stored link key status %d", result.status); + result.max_keys = 0; + result.read_keys = 0; + } + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_write_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the write stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_write_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_WRITE_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_WRITE_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event */ + STREAM_TO_UINT8 (result.status, p); + STREAM_TO_UINT8 (result.num_keys, p); + + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_delete_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the delete stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_delete_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_DELETE_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_DELETE_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event */ + STREAM_TO_UINT8 (result.status, p); + STREAM_TO_UINT16 (result.num_keys, p); + + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_return_link_keys_evt +** +** Description This function is called when the return link keys event +** is received from the HCI for the read stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_return_link_keys_evt (tBTM_RETURN_LINK_KEYS_EVT *result) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + UINT8 i, *p, *p1; + UINT8 bd_addr[BD_ADDR_LEN]; + UINT8 link_key[LINK_KEY_LEN]; + + /* Call the call back to pass the link keys to application */ + if (p_cb) + { + /* Change the BD addr and Link key in to big endian order */ + p = (UINT8 *)(result + 1); + for (i=0; inum_keys; i++) + { + /* Initialize the backup pointer */ + p1 = p; + + /* Extract the BD Addr and Link Key */ + REVERSE_STREAM_TO_ARRAY(bd_addr, p1, BD_ADDR_LEN); + REVERSE_STREAM_TO_ARRAY(link_key, p1, LINK_KEY_LEN); + + /* Write the BD Addr and Link Key back in big endian format */ + ARRAY_TO_STREAM(p, bd_addr, BD_ADDR_LEN); + ARRAY_TO_STREAM(p, link_key, LINK_KEY_LEN); + } + + (*p_cb)(result); + } +} + + + +/******************************************************************************* +** +** Function btm_report_device_status +** +** Description This function is called when there is a change in the device +** status. This function will report the new device status to +** the application +** +** Returns void +** +*******************************************************************************/ +void btm_report_device_status (tBTM_DEV_STATUS status) +{ + tBTM_DEV_STATUS_CB *p_cb = btm_cb.devcb.p_dev_status_cb; + + /* Call the call back to pass the device status to application */ + if (p_cb) + (*p_cb)(status); +} + + diff --git a/stack/btm/btm_inq.c b/stack/btm/btm_inq.c new file mode 100644 index 0000000..5598562 --- /dev/null +++ b/stack/btm/btm_inq.c @@ -0,0 +1,3265 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle inquiries. These include + * setting discoverable mode, controlling the mode of the Baseband, and + * maintaining a small database of inquiry responses, with API for people + * to browse it. + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "bt_types.h" +#include "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" + +#define BTM_INQ_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */ + +/* TRUE to enable DEBUG traces for btm_inq */ +#ifndef BTM_INQ_DEBUG +#define BTM_INQ_DEBUG FALSE +#endif +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ +static const LAP general_inq_lap = {0x9e,0x8b,0x33}; +static const LAP limited_inq_lap = {0x9e,0x8b,0x00}; + +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) +#ifndef BTM_EIR_UUID_LKUP_TBL +const UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES] = +{ + UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER, +/* UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR, */ +/* UUID_SERVCLASS_PUBLIC_BROWSE_GROUP, */ + UUID_SERVCLASS_SERIAL_PORT, + UUID_SERVCLASS_LAN_ACCESS_USING_PPP, + UUID_SERVCLASS_DIALUP_NETWORKING, + UUID_SERVCLASS_IRMC_SYNC, + UUID_SERVCLASS_OBEX_OBJECT_PUSH, + UUID_SERVCLASS_OBEX_FILE_TRANSFER, + UUID_SERVCLASS_IRMC_SYNC_COMMAND, + UUID_SERVCLASS_HEADSET, + UUID_SERVCLASS_CORDLESS_TELEPHONY, + UUID_SERVCLASS_AUDIO_SOURCE, + UUID_SERVCLASS_AUDIO_SINK, + UUID_SERVCLASS_AV_REM_CTRL_TARGET, +/* UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, */ + UUID_SERVCLASS_AV_REMOTE_CONTROL, +/* UUID_SERVCLASS_VIDEO_CONFERENCING, */ + UUID_SERVCLASS_INTERCOM, + UUID_SERVCLASS_FAX, + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, +/* UUID_SERVCLASS_WAP, */ +/* UUID_SERVCLASS_WAP_CLIENT, */ + UUID_SERVCLASS_PANU, + UUID_SERVCLASS_NAP, + UUID_SERVCLASS_GN, + UUID_SERVCLASS_DIRECT_PRINTING, +/* UUID_SERVCLASS_REFERENCE_PRINTING, */ + UUID_SERVCLASS_IMAGING, + UUID_SERVCLASS_IMAGING_RESPONDER, + UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, + UUID_SERVCLASS_IMAGING_REF_OBJECTS, + UUID_SERVCLASS_HF_HANDSFREE, + UUID_SERVCLASS_AG_HANDSFREE, + UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE, +/* UUID_SERVCLASS_REFLECTED_UI, */ + UUID_SERVCLASS_BASIC_PRINTING, + UUID_SERVCLASS_PRINTING_STATUS, + UUID_SERVCLASS_HUMAN_INTERFACE, + UUID_SERVCLASS_CABLE_REPLACEMENT, + UUID_SERVCLASS_HCRP_PRINT, + UUID_SERVCLASS_HCRP_SCAN, +/* UUID_SERVCLASS_COMMON_ISDN_ACCESS, */ +/* UUID_SERVCLASS_VIDEO_CONFERENCING_GW, */ +/* UUID_SERVCLASS_UDI_MT, */ +/* UUID_SERVCLASS_UDI_TA, */ +/* UUID_SERVCLASS_VCP, */ + UUID_SERVCLASS_SAP, + UUID_SERVCLASS_PBAP_PCE, + UUID_SERVCLASS_PBAP_PSE, + UUID_SERVCLASS_PHONE_ACCESS, + UUID_SERVCLASS_HEADSET_HS, + UUID_SERVCLASS_PNP_INFORMATION, +/* UUID_SERVCLASS_GENERIC_NETWORKING, */ +/* UUID_SERVCLASS_GENERIC_FILETRANSFER, */ +/* UUID_SERVCLASS_GENERIC_AUDIO, */ +/* UUID_SERVCLASS_GENERIC_TELEPHONY, */ +/* UUID_SERVCLASS_UPNP_SERVICE, */ +/* UUID_SERVCLASS_UPNP_IP_SERVICE, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_PAN, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_LAP, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP, */ + UUID_SERVCLASS_VIDEO_SOURCE, + UUID_SERVCLASS_VIDEO_SINK, +/* UUID_SERVCLASS_VIDEO_DISTRIBUTION */ + UUID_SERVCLASS_MESSAGE_ACCESS, + UUID_SERVCLASS_MESSAGE_NOTIFICATION, + UUID_SERVCLASS_HDP_SOURCE, + UUID_SERVCLASS_HDP_SINK +}; +#else +/* +If customized UUID look-up table needs to be used, +the followings should be defined in bdroid_buildcfg.h. +BTM_EIR_UUID_LKUP_TBL = +BTM_EIR_MAX_SERVICES = +*/ +#if (BTM_EIR_MAX_SERVICES == 0) +const UINT16 BTM_EIR_UUID_LKUP_TBL[]; +#else +extern UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES]; +#endif +#endif +#endif /* BTM_EIR_UUID_LKUP_TBL*/ + +/********************************************************************************/ +/* 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 btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq); +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, tBTM_INQ_FILT_COND *p_filt_cond); +static void btm_clr_inq_result_flt (void); + +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ); +#endif +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +static void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ); +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ); +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ); +#endif + +/******************************************************************************* +** +** Function BTM_SetDiscoverability +** +** Description This function is called to set the device into or out of +** discoverable mode. Discoverable mode means inquiry +** scans are enabled. If a value of '0' is entered for window or +** interval, the default values are used. +** +** Returns BTM_SUCCESS if successful +** BTM_BUSY if a setting of the filter is already in progress +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDiscoverability (UINT16 inq_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + UINT16 service_class; + UINT8 *p_cod; + UINT8 major, minor; + DEV_CLASS cod; + LAP temp_lap[2]; + BOOLEAN is_limited; + BOOLEAN cod_limited; + + BTM_TRACE_API0 ("BTM_SetDiscoverability"); +#if (BLE_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + if (btm_ble_set_discoverability((UINT16)(inq_mode)) + == BTM_SUCCESS) + { + btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_BLE_DISCOVERABLE_MASK); + btm_cb.btm_inq_vars.discoverable_mode |= (inq_mode & BTM_BLE_CONNECTABLE_MASK); + } + inq_mode &= ~BTM_BLE_DISCOVERABLE_MASK; +#endif + + /*** Check mode parameter ***/ + if (inq_mode > BTM_MAX_DISCOVERABLE) + return (BTM_ILLEGAL_VALUE); + + /* Make sure the controller is active */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + /* If the window and/or interval is '0', set to default values */ + if (!window) + window = BTM_DEFAULT_DISC_WINDOW; + + if (!interval) + interval = BTM_DEFAULT_DISC_INTERVAL; + + BTM_TRACE_API3 ("BTM_SetDiscoverability: mode %d [NonDisc-0, Lim-1, Gen-2], window 0x%04x, interval 0x%04x", + inq_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (inq_mode != BTM_NON_DISCOVERABLE) + { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_INQUIRYSCAN_WINDOW || + window > HCI_MAX_INQUIRYSCAN_WINDOW || + interval < HCI_MIN_INQUIRYSCAN_INTERVAL || + interval > HCI_MAX_INQUIRYSCAN_INTERVAL || + window > interval) + { + return (BTM_ILLEGAL_VALUE); + } + } + + /* Set the IAC if needed */ + if (inq_mode != BTM_NON_DISCOVERABLE) + { + if (inq_mode & BTM_LIMITED_DISCOVERABLE) + { + /* Use the GIAC and LIAC codes for limited discoverable mode */ + memcpy (temp_lap[0], limited_inq_lap, LAP_LEN); + memcpy (temp_lap[1], general_inq_lap, LAP_LEN); + + if (!btsnd_hcic_write_cur_iac_lap (2, (LAP * const) temp_lap)) + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + else + { + if (!btsnd_hcic_write_cur_iac_lap (1, (LAP * const) &general_inq_lap)) + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + } + + /* Send down the inquiry scan window and period if changed */ + if ((window != btm_cb.btm_inq_vars.inq_scan_window) || + (interval != btm_cb.btm_inq_vars.inq_scan_period)) + { + if (btsnd_hcic_write_inqscan_cfg (interval, window)) + { + btm_cb.btm_inq_vars.inq_scan_window = window; + btm_cb.btm_inq_vars.inq_scan_period = interval; + } + else + return (BTM_NO_RESOURCES); + } + + if (btm_cb.btm_inq_vars.connectable_mode & BTM_CONNECTABLE_MASK) + scan_mode |= HCI_PAGE_SCAN_ENABLED; + + if (btsnd_hcic_write_scan_enable (scan_mode)) + { + btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_DISCOVERABLE_MASK); + btm_cb.btm_inq_vars.discoverable_mode |= inq_mode; + } + else + return (BTM_NO_RESOURCES); + + /* Change the service class bit if mode has changed */ + p_cod = BTM_ReadDeviceClass(); + BTM_COD_SERVICE_CLASS(service_class, p_cod); + is_limited = (inq_mode & BTM_LIMITED_DISCOVERABLE) ? TRUE : FALSE; + cod_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? TRUE : FALSE; + if (is_limited ^ cod_limited) + { + BTM_COD_MINOR_CLASS(minor, p_cod ); + BTM_COD_MAJOR_CLASS(major, p_cod ); + if (is_limited) + service_class |= BTM_COD_SERVICE_LMTD_DISCOVER; + else + service_class &= ~BTM_COD_SERVICE_LMTD_DISCOVER; + + FIELDS_TO_COD(cod, minor, major, service_class); + (void) BTM_SetDeviceClass (cod); + } + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetInquiryScanType +** +** Description This function is called to set the iquiry scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryScanType (UINT16 scan_type) +{ + + BTM_TRACE_API0 ("BTM_SetInquiryScanType"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) + return (BTM_ILLEGAL_VALUE); + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.inq_scan_type) + { + if (BTM_IsDeviceUp()) + { + if (btsnd_hcic_write_inqscan_type ((UINT8)scan_type)) + btm_cb.btm_inq_vars.inq_scan_type = scan_type; + else + return (BTM_NO_RESOURCES); + } + else return (BTM_WRONG_MODE); + } + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetPageScanType +** +** Description This function is called to set the page scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPageScanType (UINT16 scan_type) +{ + BTM_TRACE_API0 ("BTM_SetPageScanType"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) + return (BTM_ILLEGAL_VALUE); + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_INTERLACED_PAGE_SCAN_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.page_scan_type) + { + if (BTM_IsDeviceUp()) + { + if (btsnd_hcic_write_pagescan_type ((UINT8)scan_type)) + btm_cb.btm_inq_vars.page_scan_type = scan_type; + else + return (BTM_NO_RESOURCES); + } + else return (BTM_WRONG_MODE); + } + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SetInquiryMode +** +** Description This function is called to set standard or with RSSI +** mode of the inquiry for local device. +** +** Output Params: mode - standard, with RSSI, extended +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryMode (UINT8 mode) +{ + BTM_TRACE_API0 ("BTM_SetInquiryMode"); + if (mode == BTM_INQ_RESULT_STANDARD) + { + /* mandatory mode */ + } + else if (mode == BTM_INQ_RESULT_WITH_RSSI) + { + if (!HCI_LMP_INQ_RSSI_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + } +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) + else if (mode == BTM_INQ_RESULT_EXTENDED) + { + if (!HCI_EXT_INQ_RSP_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + } +#endif + else + return (BTM_ILLEGAL_VALUE); + + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if (!btsnd_hcic_write_inquiry_mode (mode)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadDiscoverability +** +** Description This function is called to read the current discoverability +** mode of the device. +** +** Output Params: p_window - current inquiry scan duration +** p_interval - current inquiry scan interval +** +** Returns BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or +** BTM_GENERAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_ReadDiscoverability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API0 ("BTM_ReadDiscoverability"); + if (p_window) + *p_window = btm_cb.btm_inq_vars.inq_scan_window; + + if (p_interval) + *p_interval = btm_cb.btm_inq_vars.inq_scan_period; + + return (btm_cb.btm_inq_vars.discoverable_mode); +} + + +/******************************************************************************* +** +** Function BTM_SetPeriodicInquiryMode +** +** Description This function is called to set the device periodic inquiry mode. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Note: We currently do not allow concurrent inquiry and periodic inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** max_delay - maximum amount of time between successive inquiries +** min_delay - minimum amount of time between successive inquiries +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPeriodicInquiryMode (tBTM_INQ_PARMS *p_inqparms, UINT16 max_delay, + UINT16 min_delay, tBTM_INQ_RESULTS_CB *p_results_cb) +{ + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API6 ("BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: %d, max: %d", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type, min_delay, max_delay); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) + return (BTM_BUSY); + + /* If illegal parameters return FALSE */ + if (p_inqparms->mode != BTM_GENERAL_INQUIRY && + p_inqparms->mode != BTM_LIMITED_INQUIRY) + return (BTM_ILLEGAL_VALUE); + + /* Verify the parameters for this command */ + if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN || + p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH || + min_delay <= p_inqparms->duration || + min_delay < BTM_PER_INQ_MIN_MIN_PERIOD || + min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || + max_delay <= min_delay || + max_delay < BTM_PER_INQ_MIN_MAX_PERIOD || + max_delay > BTM_PER_INQ_MAX_MAX_PERIOD) + { + return (BTM_ILLEGAL_VALUE); + } + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; + p_inq->per_min_delay = min_delay; + p_inq->per_max_delay = max_delay; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->p_inq_results_cb = p_results_cb; + + p_inq->inq_active = (UINT8)((p_inqparms->mode == BTM_LIMITED_INQUIRY) ? + (BTM_LIMITED_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE) : + (BTM_GENERAL_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE)); + +#if (defined(BTM_BYPASS_EVENT_FILTERING) && BTM_BYPASS_EVENT_FILTERING == TRUE) + BTM_TRACE_WARNING0("BTM: Bypassing event filtering..."); + + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + status = BTM_CMD_STARTED; +#else + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + if (p_inqparms->filter_cond_type != BTM_CLR_INQUIRY_FILTER) + { + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + } + else /* The filter is not being used so simply clear it; the inquiry can start after this operation */ + p_inq->state = BTM_INQ_SET_FILT_STATE; + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, &p_inqparms->filter_cond)) != BTM_CMD_STARTED) + { + /* If set filter command is not succesful reset the state */ + p_inq->p_inq_results_cb = NULL; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + } + +#endif + return (status); +} + + +/******************************************************************************* +** +** Function BTM_CancelPeriodicInquiry +** +** Description This function cancels a periodic inquiry +** +** Returns +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelPeriodicInquiry(void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_STATUS status = BTM_SUCCESS; + BTM_TRACE_API0 ("BTM_CancelPeriodicInquiry called"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only cancel if one is active */ + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE; + btm_cb.btm_inq_vars.p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + + if (!btsnd_hcic_exit_per_inq ()) + status = BTM_NO_RESOURCES; + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if(p_inq->inqfilt_active) + p_inq->pending_filt_complete_event++; + + p_inq->inqfilt_active = FALSE; + p_inq->inq_counter++; + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_SetConnectability +** +** Description This function is called to set the device into or out of +** connectable mode. Discoverable mode means page scans enabled. +** +** Returns BTM_SUCCESS if successful +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetConnectability (UINT16 page_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API0 ("BTM_SetConnectability"); + +#if (BLE_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + if (btm_ble_set_connectability(page_mode) == BTM_SUCCESS) + { + p_inq->connectable_mode &= (~BTM_BLE_CONNECTABLE_MASK); + p_inq->connectable_mode |= (page_mode & BTM_BLE_CONNECTABLE_MASK); + } + page_mode &= ~BTM_BLE_CONNECTABLE_MASK; + +#endif + + /*** Check mode parameter ***/ + if (page_mode != BTM_NON_CONNECTABLE && page_mode != BTM_CONNECTABLE) + return (BTM_ILLEGAL_VALUE); + + /* Make sure the controller is active */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + /* If the window and/or interval is '0', set to default values */ + if (!window) + window = BTM_DEFAULT_CONN_WINDOW; + + if (!interval) + interval = BTM_DEFAULT_CONN_INTERVAL; + + BTM_TRACE_API3 ("BTM_SetConnectability: mode %d [NonConn-0, Conn-1], window 0x%04x, interval 0x%04x", + page_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (page_mode == BTM_CONNECTABLE) + { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_PAGESCAN_WINDOW || + window > HCI_MAX_PAGESCAN_WINDOW || + interval < HCI_MIN_PAGESCAN_INTERVAL || + interval > HCI_MAX_PAGESCAN_INTERVAL || + window > interval) + { + return (BTM_ILLEGAL_VALUE); + } + + scan_mode |= HCI_PAGE_SCAN_ENABLED; + } + + if ((window != p_inq->page_scan_window) || + (interval != p_inq->page_scan_period)) + { + p_inq->page_scan_window = window; + p_inq->page_scan_period = interval; + if (!btsnd_hcic_write_pagescan_cfg (interval, window)) + return (BTM_NO_RESOURCES); + } + + /* Keep the inquiry scan as previouosly set */ + if (p_inq->discoverable_mode & BTM_DISCOVERABLE_MASK) + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + + if (btsnd_hcic_write_scan_enable (scan_mode)) + { + p_inq->connectable_mode &= (~BTM_CONNECTABLE_MASK); + p_inq->connectable_mode |= page_mode; + + return (BTM_SUCCESS); + } + + return (BTM_NO_RESOURCES); +} + + +/******************************************************************************* +** +** Function BTM_ReadConnectability +** +** Description This function is called to read the current discoverability +** mode of the device. +** Output Params p_window - current page scan duration +** p_interval - current time between page scans +** +** Returns BTM_NON_CONNECTABLE or BTM_CONNECTABLE +** +*******************************************************************************/ +UINT16 BTM_ReadConnectability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API0 ("BTM_ReadConnectability"); + if (p_window) + *p_window = btm_cb.btm_inq_vars.page_scan_window; + + if (p_interval) + *p_interval = btm_cb.btm_inq_vars.page_scan_period; + + return (btm_cb.btm_inq_vars.connectable_mode); +} + + + +/******************************************************************************* +** +** Function BTM_IsInquiryActive +** +** Description This function returns a bit mask of the current inquiry state +** +** Returns BTM_INQUIRY_INACTIVE if inactive (0) +** BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active +** BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active +** BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active +** +*******************************************************************************/ +UINT16 BTM_IsInquiryActive (void) +{ + BTM_TRACE_API0 ("BTM_IsInquiryActive"); + + return(btm_cb.btm_inq_vars.inq_active); +} + + + +/******************************************************************************* +** +** Function BTM_CancelInquiry +** +** Description This function cancels an inquiry if active +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelInquiry(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API0 ("BTM_CancelInquiry called"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only cancel if not in periodic mode, otherwise the caller should call BTM_CancelPeriodicMode */ + if (p_inq->inq_active && + (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE))) + { + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; /* Do not notify caller anymore */ + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; /* Do not notify caller anymore */ + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if (p_inq->inqfilt_active) + { + p_inq->inqfilt_active = FALSE; + p_inq->pending_filt_complete_event++; + } + /* Initiate the cancel inquiry */ + else + { + if (!btsnd_hcic_inq_cancel()) + status = BTM_NO_RESOURCES; +#if BLE_INCLUDED == TRUE + if ((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) + btm_ble_stop_scan(); +#endif + } + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + /* Do not send the BUSY_LEVEL event yet. Wait for the cancel_complete event + * and then send the BUSY_LEVEL event + * btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + */ +#endif + + p_inq->inq_counter++; + btm_clr_inq_result_flt(); + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_StartInquiry +** +** Description This function is called to start an inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** p_results_cb - Pointer to the callback routine which gets called +** upon receipt of an inquiry result. If this field is +** NULL, the application is not notified. +** +** p_cmpl_cb - Pointer to the callback routine which gets called +** upon completion. If this field is NULL, the +** application is not notified when completed. +** Returns tBTM_STATUS +** BTM_CMD_STARTED if successfully initiated +** BTM_BUSY if already in progress +** BTM_ILLEGAL_VALUE if parameter(s) are out of range +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb, + tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API4 ("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type); + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) + return (BTM_BUSY); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK)!= BTM_GENERAL_INQUIRY && + (p_inqparms->mode & BTM_BR_INQUIRY_MASK)!= BTM_LIMITED_INQUIRY) + return (BTM_ILLEGAL_VALUE); + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; +#if (BLE_INCLUDED == TRUE) + p_inq->inqparms.mode = (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) | (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); +#else + p_inq->inqparms.mode = (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); +#endif + + /* Initialize the inquiry variables */ + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->p_inq_cmpl_cb = p_cmpl_cb; + p_inq->p_inq_results_cb = p_results_cb; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->inq_active = (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + + BTM_TRACE_DEBUG1("BTM_StartInquiry: p_inq->inq_active = 0x%02x", p_inq->inq_active); + +/* start LE inquiry here if requested */ +#if BLE_INCLUDED == TRUE + if (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) + { + /* BLE for now does not support filter condition for inquiry */ + if (btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK), + p_inq->inqparms.duration) != BTM_SUCCESS) + { + BTM_TRACE_ERROR0("Err Starting LE Inquiry."); + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + } + + p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK; + + BTM_TRACE_DEBUG1("BTM_StartInquiry: mode = %02x", p_inqparms->mode); + } +#endif /* end of BLE_INCLUDED */ + +#if (defined(BTM_BYPASS_EVENT_FILTERING) && BTM_BYPASS_EVENT_FILTERING == TRUE) + BTM_TRACE_WARNING0("BTM: Bypassing event filtering..."); + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + status = BTM_CMD_STARTED; +#else + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + switch (p_inqparms->filter_cond_type) + { + case BTM_CLR_INQUIRY_FILTER: + p_inq->state = BTM_INQ_SET_FILT_STATE; + break; + + case BTM_FILTER_COND_DEVICE_CLASS: + case BTM_FILTER_COND_BD_ADDR: + /* The filter is not being used so simply clear it; + the inquiry can start after this operation */ + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + /* =============>>>> adding LE filtering here ????? */ + break; + + default: + return (BTM_ILLEGAL_VALUE); + } + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, &p_inqparms->filter_cond)) != BTM_CMD_STARTED) + p_inq->state = BTM_INQ_INACTIVE_STATE; +#endif + return (status); +} + + +/******************************************************************************* +** +** Function BTM_ReadRemoteDeviceName +** +** Description This function initiates a remote device HCI command to the +** controller and calls the callback when the process has completed. +** +** Input Params: remote_bda - device address of name to retrieve +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_BUSY if already in progress +** BTM_UNKNOWN_ADDR if device address is bad +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQ_INFO *p_cur = NULL; + tINQ_DB_ENT *p_i; + +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + BTM_TRACE_API6 ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* Use the remote device's clock offset if it is in the local inquiry database */ + if ((p_i = btm_inq_db_find (remote_bda)) != NULL) + { + p_cur = &p_i->inq_info; + +#if (BTM_INQ_GET_REMOTE_NAME == TRUE) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + } + BTM_TRACE_API0 ("no device found in inquiry db"); + +#if (BLE_INCLUDED == TRUE) + BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + return btm_ble_read_remote_name(remote_bda, p_cur, p_cb); + } + else +#endif + + return (btm_initiate_rem_name (remote_bda, p_cur, BTM_RMT_NAME_EXT, + BTM_EXT_RMT_NAME_TIMEOUT, p_cb)); +} + +/******************************************************************************* +** +** Function BTM_CancelRemoteDeviceName +** +** Description This function initiates the cancel request for the specified +** remote device. +** +** Input Params: None +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if there is not an active remote name request. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelRemoteDeviceName (void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + BTM_TRACE_API0 ("BTM_CancelRemoteDeviceName()"); + + /* Make sure there is not already one in progress */ + if (p_inq->remname_active) + { +#if BLE_INCLUDED == TRUE + BTM_ReadDevInfo(p_inq->remname_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (btm_ble_cancel_remote_name(p_inq->remname_bda)) + return (BTM_CMD_STARTED); + else + return (BTM_UNKNOWN_ADDR); + } + else +#endif + if (btsnd_hcic_rmt_name_req_cancel (p_inq->remname_bda)) + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); + } + else + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_InqFirstResult +** +** Description This function looks through the inquiry database for the first +** used entrysince the LAST inquiry. This is used in conjunction +** with BTM_InqNext by applications as a way to walk through the +** inquiry results database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqFirstResult (void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + UINT32 cur_inq_count = btm_cb.btm_inq_vars.inq_counter - 1; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use && p_ent->inq_count == cur_inq_count) + return (&p_ent->inq_info); + } + + /* If here, no used entry found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqNextResult +** +** Description This function looks through the inquiry database for the next +** used entrysince the LAST inquiry. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqNextResult (tBTM_INQ_INFO *p_cur) +{ + tINQ_DB_ENT *p_ent; + UINT16 inx; + UINT32 cur_inq_count = btm_cb.btm_inq_vars.inq_counter - 1; + + if (p_cur) + { + p_ent = (tINQ_DB_ENT *) ((UINT8 *)p_cur - offsetof (tINQ_DB_ENT, inq_info)); + inx = (UINT16)((p_ent - btm_cb.btm_inq_vars.inq_db) + 1); + + for (p_ent = &btm_cb.btm_inq_vars.inq_db[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) + { + if (p_ent->in_use && p_ent->inq_count == cur_inq_count) + return (&p_ent->inq_info); + } + + /* If here, more entries found */ + return ((tBTM_INQ_INFO *)NULL); + } + else + return (BTM_InqDbFirst()); +} + + +/******************************************************************************* +** +** Function BTM_InqDbRead +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address. This is the application's +** interface to get the inquiry details of a specific BD address. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbRead (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + BTM_TRACE_API6 ("BTM_InqDbRead: bd addr [%02x%02x%02x%02x%02x%02x]", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if ((p_ent->in_use) && (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + return (&p_ent->inq_info); + } + + /* If here, not found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqDbFirst +** +** Description This function looks through the inquiry database for the first +** used entry, and returns that. This is used in conjunction with +** BTM_InqDbNext by applications as a way to walk through the +** inquiry database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbFirst (void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use) + return (&p_ent->inq_info); + } + + /* If here, no used entry found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqDbNext +** +** Description This function looks through the inquiry database for the next +** used entry, and returns that. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbNext (tBTM_INQ_INFO *p_cur) +{ + tINQ_DB_ENT *p_ent; + UINT16 inx; + + if (p_cur) + { + p_ent = (tINQ_DB_ENT *) ((UINT8 *)p_cur - offsetof (tINQ_DB_ENT, inq_info)); + inx = (UINT16)((p_ent - btm_cb.btm_inq_vars.inq_db) + 1); + + for (p_ent = &btm_cb.btm_inq_vars.inq_db[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) + { + if (p_ent->in_use) + return (&p_ent->inq_info); + } + + /* If here, more entries found */ + return ((tBTM_INQ_INFO *)NULL); + } + else + return (BTM_InqDbFirst()); +} + + +/******************************************************************************* +** +** Function BTM_ClearInqDb +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns BTM_BUSY if an inquiry, get remote name, or event filter +** is active, otherwise BTM_SUCCESS +** +*******************************************************************************/ +tBTM_STATUS BTM_ClearInqDb (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + /* If an inquiry or remote name is in progress return busy */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE || + p_inq->inqfilt_active) + return (BTM_BUSY); + + btm_clr_inq_db(p_bda); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_ReadNumInqDbEntries +** +** Returns This function returns the number of entries in the inquiry database. +** +*******************************************************************************/ +UINT8 BTM_ReadNumInqDbEntries (void) +{ + UINT8 num_entries; + UINT8 num_results; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (num_entries = 0, num_results = 0; num_entries < BTM_INQ_DB_SIZE; num_entries++, p_ent++) + { + if (p_ent->in_use) + num_results++; + } + + return (num_results); +} + + +/******************************************************************************* +** +** Function BTM_InquiryRegisterForChanges +** +** Returns This function is called to register a callback for when the +** inquiry database changes, i.e. new entry or entry deleted. +** +*******************************************************************************/ +tBTM_STATUS BTM_InquiryRegisterForChanges (tBTM_INQ_DB_CHANGE_CB *p_cb) +{ + if (!p_cb) + btm_cb.btm_inq_vars.p_inq_change_cb = NULL; + else if (btm_cb.btm_inq_vars.p_inq_change_cb) + return (BTM_BUSY); + else + btm_cb.btm_inq_vars.p_inq_change_cb = p_cb; + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SetInquiryFilterCallback +** +** Description Host can register to be asked whenever an inquiry result +** is received. If host does not like the device no name +** request is issued for the device +** +** Returns void +** +*******************************************************************************/ +void BTM_SetInquiryFilterCallback (tBTM_FILTER_CB *p_callback) +{ + btm_cb.p_inq_filter_cb = p_callback; +} + +/******************************************************************************* +** +** Function BTM_ReadInquiryRspTxPower +** +** Description This command will read the inquiry Transmit Power level used +** to transmit the FHS and EIR data packets. +** This can be used directly in the Tx Power Level EIR data type. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadInquiryRspTxPower (tBTM_CMPL_CB *p_cb) +{ + if (btm_cb.devcb.p_txpwer_cmpl_cb) + return (BTM_BUSY); + + btu_start_timer (&btm_cb.devcb.txpwer_timer, BTU_TTYPE_BTM_ACL, BTM_INQ_REPLY_TIMEOUT ); + + + btm_cb.devcb.p_txpwer_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_inq_tx_power ()) + { + btm_cb.devcb.p_txpwer_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + return (BTM_NO_RESOURCES); + } + else + return (BTM_CMD_STARTED); +} +/******************************************************************************* +** +** Function BTM_WriteInquiryTxPower +** +** Description This command is used to write the inquiry transmit power level +** used to transmit the inquiry (ID) data packets. The Controller +** should use the supported TX power level closest to the Tx_Power +** parameter. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteInquiryTxPower (INT8 tx_power) +{ + tBTM_STATUS status = BTM_SUCCESS; + + if (tx_power < BTM_MIN_INQ_TX_POWER || tx_power > BTM_MAX_INQ_TX_POWER) + { + status = BTM_ILLEGAL_VALUE; + } + else if (!btsnd_hcic_write_inq_tx_power(tx_power)) + status = BTM_NO_RESOURCES; + + return status; +} +/********************************************************************************* +********************************************************************************** +** ** +** BTM Internal Inquiry Functions ** +** ** +********************************************************************************** +*********************************************************************************/ +/******************************************************************************* +** +** Function btm_inq_db_reset +** +** Description This function is called at at reset to clear the inquiry +** database & pending callback. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_reset (void) +{ + tBTM_REMOTE_DEV_NAME rem_name; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 num_responses; + UINT8 temp_inq_active; + tBTM_STATUS status; + + btu_stop_timer (&p_inq->inq_timer_ent); + + /* If an inquiry or periodic inquiry is active, reset the mode to inactive */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE) + { + temp_inq_active = p_inq->inq_active; /* Save so state can change BEFORE + callback is called */ + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + + /* If not a periodic inquiry, the complete callback must be called to notify caller */ + if (temp_inq_active == BTM_LIMITED_INQUIRY_ACTIVE || + temp_inq_active == BTM_GENERAL_INQUIRY_ACTIVE) + { + if (p_inq->p_inq_cmpl_cb) + { + num_responses = 0; + (*p_inq->p_inq_cmpl_cb)(&num_responses); + } + } + } + + /* Cancel a remote name request if active, and notify the caller (if waiting) */ + if (p_inq->remname_active ) + { + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + if (p_inq->p_remname_cmpl_cb) + { + rem_name.status = BTM_DEV_RESET; + + (*p_inq->p_remname_cmpl_cb)(&rem_name); + p_inq->p_remname_cmpl_cb = NULL; + } + } + + /* Cancel an inquiry filter request if active, and notify the caller (if waiting) */ + if (p_inq->inqfilt_active) + { + p_inq->inqfilt_active = FALSE; + + if (p_inq->p_inqfilter_cmpl_cb) + { + status = BTM_DEV_RESET; + (*p_inq->p_inqfilter_cmpl_cb)(&status); + } + } + + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->pending_filt_complete_event = 0; + p_inq->p_inq_results_cb = NULL; + btm_clr_inq_db(NULL); /* Clear out all the entries in the database */ + btm_clr_inq_result_flt(); + + p_inq->discoverable_mode = BTM_NON_DISCOVERABLE; + p_inq->connectable_mode = BTM_NON_CONNECTABLE; + p_inq->page_scan_type = BTM_SCAN_TYPE_STANDARD; + p_inq->inq_scan_type = BTM_SCAN_TYPE_STANDARD; + +#if BLE_INCLUDED == TRUE + p_inq->discoverable_mode |= BTM_BLE_NON_DISCOVERABLE; + p_inq->connectable_mode |= BTM_BLE_NON_CONNECTABLE; +#endif + return; +} + + +/********************************************************************************* +** +** Function btm_inq_db_init +** +** Description This function is called at startup to initialize the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.btm_inq_vars, 0, sizeof (tBTM_INQUIRY_VAR_ST)); +#endif + btm_cb.btm_inq_vars.no_inc_ssp = BTM_NO_SSP_ON_INQUIRY; +} + +/********************************************************************************* +** +** Function btm_inq_stop_on_ssp +** +** Description This function is called on incoming SSP +** +** Returns void +** +*******************************************************************************/ +void btm_inq_stop_on_ssp(void) +{ + UINT8 normal_active = (BTM_GENERAL_INQUIRY_ACTIVE|BTM_LIMITED_INQUIRY_ACTIVE); + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG4 ("btm_inq_stop_on_ssp: no_inc_ssp=%d inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.no_inc_ssp, btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + if (btm_cb.btm_inq_vars.no_inc_ssp) + { + if (btm_cb.btm_inq_vars.state == BTM_INQ_ACTIVE_STATE) + { + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + BTM_CancelPeriodicInquiry(); + } + else if (btm_cb.btm_inq_vars.inq_active & normal_active) + { + /* can not call BTM_CancelInquiry() here. We need to report inquiry complete evt */ + btsnd_hcic_inq_cancel(); + } + } + /* do not allow inquiry to start */ + btm_cb.btm_inq_vars.inq_active |= BTM_SSP_INQUIRY_ACTIVE; + } +} + +/********************************************************************************* +** +** Function btm_inq_clear_ssp +** +** Description This function is called when pairing_state becomes idle +** +** Returns void +** +*******************************************************************************/ +void btm_inq_clear_ssp(void) +{ + btm_cb.btm_inq_vars.inq_active &= ~BTM_SSP_INQUIRY_ACTIVE; +} + +/********************************************************************************* +** +** Function btm_clr_inq_db +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns void +** +*******************************************************************************/ +void btm_clr_inq_db (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_DB_ENT *p_ent = p_inq->inq_db; + UINT16 xx; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG2 ("btm_clr_inq_db: inq_active:0x%x state:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use) + { + /* If this is the specified BD_ADDR or clearing all devices */ + if (p_bda == NULL || + (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + { + p_ent->in_use = FALSE; +#if (BTM_INQ_GET_REMOTE_NAME == TRUE) + p_ent->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_ent->inq_info, FALSE); + } + } + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG2 ("inq_active:0x%x state:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif +} + + +/******************************************************************************* +** +** Function btm_clr_inq_result_flt +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +static void btm_clr_inq_result_flt (void) +{ +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_inq->p_bd_db) + { + GKI_freebuf(p_inq->p_bd_db); + p_inq->p_bd_db = NULL; + } + p_inq->num_bd_entries = 0; + p_inq->max_bd_entries = 0; +#endif +} + +/******************************************************************************* +** +** Function btm_inq_find_bdaddr +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda) +{ +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_BDADDR *p_db = &p_inq->p_bd_db[0]; + UINT16 xx; + + /* Don't bother searching, database doesn't exist or periodic mode */ + if ((p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) || !p_db) + return (FALSE); + + for (xx = 0; xx < p_inq->num_bd_entries; xx++, p_db++) + { + if (!memcmp(p_db->bd_addr, p_bda, BD_ADDR_LEN) + && p_db->inq_count == p_inq->inq_counter) + return (TRUE); + } + + if (xx < p_inq->max_bd_entries) + { + p_db->inq_count = p_inq->inq_counter; + memcpy(p_db->bd_addr, p_bda, BD_ADDR_LEN); + p_inq->num_bd_entries++; + } + +#endif + /* If here, New Entry */ + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_inq_db_find +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if ((p_ent->in_use) && (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + return (p_ent); + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function btm_inq_db_new +** +** Description This function looks through the inquiry database for an unused +** entry. If no entry is free, it allocates the oldest entry. +** +** Returns pointer to entry +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_old = btm_cb.btm_inq_vars.inq_db; + UINT32 ot = 0xFFFFFFFF; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (!p_ent->in_use) + { + memset (p_ent, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_ent->in_use = TRUE; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + p_ent->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + return (p_ent); + } + + if (p_ent->time_of_resp < ot) + { + p_old = p_ent; + ot = p_ent->time_of_resp; + } + } + + /* If here, no free entry found. Return the oldest. */ + + /* Before deleting the oldest, if anyone is registered for change */ + /* notifications, then tell him we are deleting an entry. */ + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_old->inq_info, FALSE); + + memset (p_old, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_old->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_old->in_use = TRUE; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + p_old->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + return (p_old); +} + + +/******************************************************************************* +** +** Function btm_set_inq_event_filter +** +** Description This function is called to set the inquiry event filter. +** It is called by either internally, or by the external API function +** (BTM_SetInqEventFilter). It is used internally as part of the +** inquiry processing. +** +** Input Params: +** filter_cond_type - this is the type of inquiry filter to apply: +** BTM_FILTER_COND_DEVICE_CLASS, +** BTM_FILTER_COND_BD_ADDR, or +** BTM_CLR_INQUIRY_FILTER +** +** p_filt_cond - this is either a BD_ADDR or DEV_CLASS depending on the +** filter_cond_type (See section 4.7.3 of Core Spec 1.0b). +** +** Returns BTM_CMD_STARTED if successfully initiated +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** +*******************************************************************************/ +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, + tBTM_INQ_FILT_COND *p_filt_cond) +{ + UINT8 condition_length = DEV_CLASS_LEN * 2; + UINT8 condition_buf[DEV_CLASS_LEN * 2]; + UINT8 *p_cond = condition_buf; /* points to the condition to pass to HCI */ + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG1 ("btm_set_inq_event_filter: filter type %d [Clear-0, COD-1, BDADDR-2]", + filter_cond_type); + BTM_TRACE_DEBUG6 (" condition [%02x%02x%02x %02x%02x%02x]", + p_filt_cond->bdaddr_cond[0], p_filt_cond->bdaddr_cond[1], p_filt_cond->bdaddr_cond[2], + p_filt_cond->bdaddr_cond[3], p_filt_cond->bdaddr_cond[4], p_filt_cond->bdaddr_cond[5]); +#endif + + /* Load the correct filter condition to pass to the lower layer */ + switch (filter_cond_type) + { + case BTM_FILTER_COND_DEVICE_CLASS: + /* copy the device class and device class fields into contiguous memory to send to HCI */ + memcpy (condition_buf, p_filt_cond->cod_cond.dev_class, DEV_CLASS_LEN); + memcpy (&condition_buf[DEV_CLASS_LEN], + p_filt_cond->cod_cond.dev_class_mask, DEV_CLASS_LEN); + + /* condition length should already be set as the default */ + break; + + case BTM_FILTER_COND_BD_ADDR: + p_cond = p_filt_cond->bdaddr_cond; + + /* condition length should already be set as the default */ + break; + + case BTM_CLR_INQUIRY_FILTER: + condition_length = 0; + break; + + default: + return (BTM_ILLEGAL_VALUE); /* Bad parameter was passed in */ + } + + btm_cb.btm_inq_vars.inqfilt_active = TRUE; + + /* Filter the inquiry results for the specified condition type and value */ + if (btsnd_hcic_set_event_filter(HCI_FILTER_INQUIRY_RESULT, filter_cond_type, + p_cond, condition_length)) + + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); +} + + +/******************************************************************************* +** +** Function btm_event_filter_complete +** +** Description This function is called when a set event filter has completed. +** Note: This routine currently only handles inquiry filters. +** Connection filters are ignored for now. +** +** Returns void +** +*******************************************************************************/ +void btm_event_filter_complete (UINT8 *p) +{ + UINT8 hci_status; + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_inqfilter_cmpl_cb; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_event_filter_complete: inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + /* If the filter complete event is from an old or cancelled request, ignore it */ + if(p_inq->pending_filt_complete_event) + { + p_inq->pending_filt_complete_event--; + return; + } + + /* Only process the inquiry filter; Ignore the connection filter until it + is used by the upper layers */ + if (p_inq->inqfilt_active == TRUE ) + { + /* Extract the returned status from the buffer */ + STREAM_TO_UINT8 (hci_status, p); + if (hci_status != HCI_SUCCESS) + { + /* If standalone operation, return the error status; if embedded in the inquiry, continue the inquiry */ + BTM_TRACE_WARNING1 ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)", hci_status); + status = BTM_ERR_PROCESSING; + } + else + status = BTM_SUCCESS; + + /* If the set filter was initiated externally (via BTM_SetInqEventFilter), call the + callback function to notify the initiator that it has completed */ + if (p_inq->state == BTM_INQ_INACTIVE_STATE) + { + p_inq->inqfilt_active = FALSE; + if (p_cb) + (*p_cb) (&status); + } + else /* An inquiry is active (the set filter command was internally generated), + process the next state of the process (Set a new filter or start the inquiry). */ + { + if(status != BTM_SUCCESS) + { + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + + /* btm_process_inq_complete() does not restore the following settings on periodic inquiry */ + p_inq->inqfilt_active = FALSE; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + return; + } + + /* Check to see if a new filter needs to be set up */ + if (p_inq->state == BTM_INQ_CLR_FILT_STATE) + { + if ((status = btm_set_inq_event_filter (p_inq->inqparms.filter_cond_type, &p_inq->inqparms.filter_cond)) == BTM_CMD_STARTED) + { + p_inq->state = BTM_INQ_SET_FILT_STATE; + } + else /* Error setting the filter: Call the initiator's callback function to indicate a failure */ + { + p_inq->inqfilt_active = FALSE; + + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + } + } + else /* Initiate the Inquiry or Periodic Inquiry */ + { + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + } + } + } +} + + +/******************************************************************************* +** +** Function btm_initiate_inquiry +** +** Description This function is called to start an inquiry or periodic inquiry +** upon completion of the setting and/or clearing of the inquiry filter. +** +** Inputs: p_inq (btm_cb.btm_inq_vars) - pointer to saved inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** Returns If an error occurs the initiator's callback is called with the error status. +** +*******************************************************************************/ +static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq) +{ + const LAP *lap; + tBTM_INQ_PARMS *p_inqparms = &p_inq->inqparms; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_initiate_inquiry: inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_EVT); +#endif + + if (p_inq->inq_active & BTM_SSP_INQUIRY_ACTIVE) + { + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + return; + } + + /* Make sure the number of responses doesn't overflow the database configuration */ + p_inqparms->max_resps = (UINT8)((p_inqparms->max_resps <= BTM_INQ_DB_SIZE) ? p_inqparms->max_resps : BTM_INQ_DB_SIZE); + + lap = (p_inq->inq_active & BTM_LIMITED_INQUIRY_ACTIVE) ? &limited_inq_lap : &general_inq_lap; + + if (p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + if (!btsnd_hcic_per_inq_mode (p_inq->per_max_delay, + p_inq->per_min_delay, + *lap, p_inqparms->duration, + p_inqparms->max_resps)) + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } + else + { +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + btm_clr_inq_result_flt(); + + /* Allocate memory to hold bd_addrs responding */ + if ((p_inq->p_bd_db = (tINQ_BDADDR *)GKI_getbuf(GKI_MAX_BUF_SIZE)) != NULL) + { + p_inq->max_bd_entries = (UINT16)(GKI_MAX_BUF_SIZE / sizeof(tINQ_BDADDR)); + memset(p_inq->p_bd_db, 0, GKI_MAX_BUF_SIZE); +/* BTM_TRACE_DEBUG1("btm_initiate_inquiry: memory allocated for %d bdaddrs", + p_inq->max_bd_entries); */ + } + + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)) +#else + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, p_inqparms->max_resps)) +#endif /* BTM_USE_INQ_RESULTS_FILTER */ + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } +} + +/******************************************************************************* +** +** Function btm_process_inq_results +** +** Description This function is called when inquiry results are received from +** the device. It updates the inquiry database. If the inquiry +** database is full, the oldest entry is discarded. +** +** Parameters inq_res_mode - BTM_INQ_RESULT_STANDARD +** BTM_INQ_RESULT_WITH_RSSI +** BTM_INQ_RESULT_EXTENDED +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode) +{ + UINT8 num_resp, xx; + BD_ADDR bda; + tINQ_DB_ENT *p_i; + tBTM_INQ_RESULTS *p_cur; + BOOLEAN is_new = TRUE; + BOOLEAN update = FALSE; + INT8 i_rssi; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + UINT8 page_scan_rep_mode = 0; + UINT8 page_scan_per_mode = 0; + UINT8 page_scan_mode = 0; + UINT8 rssi = 0; + DEV_CLASS dc; + UINT16 clock_offset; +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p_eir_data = NULL; +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + UINT8 remote_name_len; +#endif +#endif + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_process_inq_results inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + /* Only process the results if the inquiry is still active */ + if (!p_inq->inq_active) + return; + + STREAM_TO_UINT8 (num_resp, p); + + for (xx = 0; xx < num_resp; xx++) + { + update = FALSE; + /* Extract inquiry results */ + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (page_scan_rep_mode, p); + STREAM_TO_UINT8 (page_scan_per_mode, p); + + if (inq_res_mode == BTM_INQ_RESULT_STANDARD) + { + STREAM_TO_UINT8(page_scan_mode, p); + } + + STREAM_TO_DEVCLASS (dc, p); + STREAM_TO_UINT16 (clock_offset, p); + if (inq_res_mode != BTM_INQ_RESULT_STANDARD) + { + STREAM_TO_UINT8(rssi, p); + } + + p_i = btm_inq_db_find (bda); + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* Only process the num_resp is smaller than max_resps. + If results are queued to BTU task while canceling inquiry, + or when more than one result is in this response, > max_resp + responses could be processed which can confuse some apps + */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp >= p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* new device response */ + && ( p_i == NULL || + /* exisiting device with BR/EDR info */ + (p_i && (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0) + ) +#endif + + ) + { +/* BTM_TRACE_WARNING0("INQ RES: Extra Response Received...ignoring"); */ + return; + } +#endif + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) + { +/* BTM_TRACE_DEBUG6("BDA seen before [%02x%02x %02x%02x %02x%02x]", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);*/ + /* By default suppose no update needed */ + i_rssi = (INT8)rssi; + + /* If this new RSSI is higher than the last one */ + if(p_inq->inqparms.report_dup && (rssi != 0) && + p_i && (i_rssi > p_i->inq_info.results.rssi || p_i->inq_info.results.rssi == 0 +#if BLE_INCLUDED == TRUE + /* BR/EDR inquiry information update */ + || (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0 +#endif + )) + { + p_cur = &p_i->inq_info.results; + BTM_TRACE_DEBUG2("update RSSI new:%d, old:%d", i_rssi, p_cur->rssi); + p_cur->rssi = i_rssi; + update = TRUE; + } + /* If we received a second Extended Inq Event for an already */ + /* discovered device, this is because for the first one EIR was not received */ + else if ((inq_res_mode == BTM_INQ_RESULT_EXTENDED) && (p_i)) + { + p_cur = &p_i->inq_info.results; + update = TRUE; + } + /* If no update needed continue with next response (if any) */ + else + continue; + } + + /* Host can be registered to verify comming BDA or DC */ + if (btm_cb.p_inq_filter_cb) + { + if (!(* btm_cb.p_inq_filter_cb) (bda, dc)) + { + continue; + } + } + + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) + { + p_i = btm_inq_db_new (bda); + is_new = TRUE; + } + + /* If an entry for the device already exists, overwrite it ONLY if it is from + a previous inquiry. (Ignore it if it is a duplicate response from the same + inquiry. + */ + else if (p_i->inq_count == p_inq->inq_counter +#if (BLE_INCLUDED == TRUE ) + && (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) +#endif + ) + is_new = FALSE; + + /* keep updating RSSI to have latest value */ + if( inq_res_mode != BTM_INQ_RESULT_STANDARD ) + p_i->inq_info.results.rssi = (INT8)rssi; + else + p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI; + + if (is_new == TRUE) + { + /* Save the info */ + p_cur = &p_i->inq_info.results; + p_cur->page_scan_rep_mode = page_scan_rep_mode; + p_cur->page_scan_per_mode = page_scan_per_mode; + p_cur->page_scan_mode = page_scan_mode; + p_cur->dev_class[0] = dc[0]; + p_cur->dev_class[1] = dc[1]; + p_cur->dev_class[2] = dc[2]; + p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + p_i->time_of_resp = GKI_get_tick_count (); + + if (p_i->inq_count != p_inq->inq_counter) + p_inq->inq_cmpl_info.num_resp++; /* A new response was found */ + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + p_cur->inq_result_type = BTM_INQ_RESULT_BR; + if (p_i->inq_count != p_inq->inq_counter) + { + p_cur->device_type = BT_DEVICE_TYPE_BREDR; + p_i->scan_rsp = FALSE; + } + else + p_cur->device_type |= BT_DEVICE_TYPE_BREDR; +#endif + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* If the number of responses found and not unlimited, issue a cancel inquiry */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && + p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* BLE scanning is active and received adv */ + && ((((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) && + p_cur->device_type == BT_DEVICE_TYPE_DUMO && p_i->scan_rsp) || + (p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) == 0) +#endif + ) + { +/* BTM_TRACE_DEBUG0("BTMINQ: Found devices, cancelling inquiry..."); */ + btsnd_hcic_inq_cancel(); + +#if BLE_INCLUDED == TRUE + if ((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) + btm_ble_stop_scan(); +#endif + + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + } +#endif + /* Initialize flag to FALSE. This flag is set/used by application */ + p_i->inq_info.appl_knows_rem_name = FALSE; + } + + if (is_new || update) + { +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + if( inq_res_mode == BTM_INQ_RESULT_EXTENDED ) + { + if((p_eir_data = BTM_CheckEirData( p, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, + &remote_name_len )) == NULL) + { + p_eir_data = BTM_CheckEirData( p, BTM_EIR_SHORTENED_LOCAL_NAME_TYPE, + &remote_name_len ); + } + + if( p_eir_data ) + { + if( remote_name_len > BTM_MAX_REM_BD_NAME_LEN ) + remote_name_len = BTM_MAX_REM_BD_NAME_LEN; + + p_i->inq_info.remote_name_len = remote_name_len; + memcpy( p_i->inq_info.remote_name, p_eir_data, p_i->inq_info.remote_name_len ); + p_i->inq_info.remote_name[p_i->inq_info.remote_name_len] = 0; + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_DONE; + } + else + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } + else +#endif + { + /* Clear out the device name so that it can be re-read */ + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } +#endif /*(BTM_INQ_GET_REMOTE_NAME==TRUE)*/ + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + if( inq_res_mode == BTM_INQ_RESULT_EXTENDED ) + { + memset( p_cur->eir_uuid, 0, + BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS/8)); + /* set bit map of UUID list from received EIR */ + btm_set_eir_uuid( p, p_cur ); + p_eir_data = p; + } + else + p_eir_data = NULL; +#endif + + /* If a callback is registered, call it with the results */ + if (p_inq_results_cb) +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, p_eir_data); +#else + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, NULL); +#endif + + /* If anyone is registered for change notifications, then tell him we added an entry. */ + if (p_inq->p_inq_change_cb) + (*p_inq->p_inq_change_cb) (&p_i->inq_info, TRUE); + } + } +} + +/******************************************************************************* +** +** Function btm_sort_inq_result +** +** Description This function is called when inquiry complete is received +** from the device to sort inquiry results based on rssi. +** +** Returns void +** +*******************************************************************************/ +void btm_sort_inq_result(void) +{ + UINT8 xx, yy, num_resp; + tINQ_DB_ENT *p_tmp = NULL; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_next = btm_cb.btm_inq_vars.inq_db+1; + int size; + + num_resp = (btm_cb.btm_inq_vars.inq_cmpl_info.num_respinq_info.results.rssi < p_next->inq_info.results.rssi) + { + memcpy (p_tmp, p_next, size); + memcpy (p_next, p_ent, size); + memcpy (p_ent, p_tmp, size); + } + } + } + + GKI_freebuf(p_tmp); + } +} + +/******************************************************************************* +** +** Function btm_process_inq_complete +** +** Description This function is called when inquiry complete is received +** from the device. Call the callback if not in periodic inquiry +** mode AND it is not NULL (The caller wants the event). +** +** The callback pass back the status and the number of responses +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_complete (UINT8 status, UINT8 mode) +{ + tBTM_CMPL_CB *p_inq_cb = btm_cb.btm_inq_vars.p_inq_cmpl_cb; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + tBTM_INQ_INFO *p_cur; + UINT8 tempstate; +#endif + + p_inq->inqparms.mode &= ~(mode); + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + /* Ignore any stray or late complete messages if the inquiry is not active */ + if (p_inq->inq_active) + { + p_inq->inq_cmpl_info.status = (tBTM_STATUS)((status == HCI_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING); + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + if (p_inq->inq_cmpl_info.status == BTM_SUCCESS) + { + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_EMPTY) + { + tempstate = p_cur->remote_name_state; + p_cur->remote_name_state = BTM_INQ_RMT_NAME_PENDING; + + if (btm_initiate_rem_name (p_cur->results.remote_bd_addr, + p_cur, BTM_RMT_NAME_INQ, + BTM_INQ_RMT_NAME_TIMEOUT, NULL) != BTM_CMD_STARTED) + p_cur->remote_name_state = tempstate; + else + return; + } + } + } +#endif + + /* Notify caller that the inquiry has completed; (periodic inquiries do not send completion events */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && p_inq->inqparms.mode == 0) + { + p_inq->state = BTM_INQ_INACTIVE_STATE; + + /* Increment so the start of a next inquiry has a new count */ + p_inq->inq_counter++; + + btm_clr_inq_result_flt(); + + if((p_inq->inq_cmpl_info.status == BTM_SUCCESS) && HCI_LMP_INQ_RSSI_SUPPORTED(btm_cb.devcb.local_features)) + { + btm_sort_inq_result(); + } + + /* Clear the results callback if set */ + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG2 ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + if (p_inq_cb) + (p_inq_cb)((tBTM_INQUIRY_CMPL *) &p_inq->inq_cmpl_info); + } + + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +} + +/******************************************************************************* +** +** Function btm_process_cancel_complete +** +** Description This function is called when inquiry cancel complete is received +** from the device.This function will also call the btm_process_inq_complete +** This function is needed to differentiate a cancel_cmpl_evt from the +** inq_cmpl_evt +** +** Returns void +** +*******************************************************************************/ +void btm_process_cancel_complete(UINT8 status, UINT8 mode) +{ +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_CANCEL_EVT); +#endif + btm_process_inq_complete(status, mode); +} +/******************************************************************************* +** +** Function btm_initiate_rem_name +** +** Description This function looks initiates a remote name request. It is called +** either by GAP or by the API call BTM_ReadRemoteDeviceName. +** +** Input Params: p_cur - pointer to an inquiry result structure (NULL if nonexistent) +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was sent to HCI. +** BTM_BUSY if already in progress +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN cmd_ok; + + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + + if (origin == BTM_RMT_NAME_SEC) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + if (cmd_ok) + return BTM_CMD_STARTED; + else + return BTM_NO_RESOURCES; + } + /* Make sure there are no two remote name requests from external API in progress */ + else if (origin == BTM_RMT_NAME_EXT) + { + if (p_inq->remname_active) + { + return (BTM_BUSY); + } + else + { + /* If there is no remote name request running,call the callback function and start timer */ + p_inq->p_remname_cmpl_cb = p_cb; + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + timeout); + + /* If the database entry exists for the device, use its clock offset */ + if (p_cur) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, + p_cur->results.page_scan_rep_mode, + p_cur->results.page_scan_mode, + (UINT16)(p_cur->results.clock_offset | + BTM_CLOCK_OFFSET_VALID)); + } + else /* Otherwise use defaults and mark the clock offset as invalid */ + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + } + if (cmd_ok) + { + p_inq->remname_active = TRUE; + return BTM_CMD_STARTED; + } + else + return BTM_NO_RESOURCES; + } + } + /* If the inquire feature is on */ +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + + else if (origin == BTM_RMT_NAME_INQ) + { + /* If the database entry exists for the device, use its clock offset */ + if (p_cur) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, + p_cur->results.page_scan_rep_mode, + p_cur->results.page_scan_mode, + (UINT16)(p_cur->results.clock_offset | + BTM_CLOCK_OFFSET_VALID)); + } + else + { + cmd_ok = FALSE + } + + if (cmd_ok) + return BTM_CMD_STARTED; + else + return BTM_NO_RESOURCES; + } +#endif + else + { + + return BTM_ILLEGAL_VALUE; + + + } + + +} + +/******************************************************************************* +** +** Function btm_process_remote_name +** +** Description This function is called when a remote name is received from +** the device. If remote names are cached, it updates the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_process_remote_name (BD_ADDR bda, BD_NAME bdn, UINT16 evt_len, UINT8 hci_status) +{ + tBTM_REMOTE_DEV_NAME rem_name = {0}; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_remname_cmpl_cb; + UINT8 *p_n1; + + UINT16 temp_evt_len; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + /*** These are only used if part of the Inquiry Process ***/ + tBTM_CMPL_CB *p_inq_cb; + tINQ_DB_ENT *p_i = NULL; + UINT8 *p_n; + tBTM_INQ_INFO *p_cur; +#endif +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + + if (bda != NULL) + { + BTM_TRACE_EVENT6("BDA %02x:%02x:%02x:%02x:%02x:%02x",bda[0], bda[1], + bda[2], bda[3], + bda[4], bda[5]); + } + + BTM_TRACE_EVENT6("Inquire BDA %02x:%02x:%02x:%02x:%02x:%02x",p_inq->remname_bda[0], p_inq->remname_bda[1], + p_inq->remname_bda[2], p_inq->remname_bda[3], + p_inq->remname_bda[4], p_inq->remname_bda[5]); + + + + /* If the inquire BDA and remote DBA are the same, then stop the timer and set the active to false */ + if ((p_inq->remname_active ==TRUE)&& + (((bda != NULL) && + (memcmp(bda, p_inq->remname_bda,BD_ADDR_LEN)==0)) || bda == NULL)) + + { +#if BLE_INCLUDED == TRUE + BTM_ReadDevInfo(p_inq->remname_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (hci_status == HCI_ERR_UNSPECIFIED) + btm_ble_cancel_remote_name(p_inq->remname_bda); + } +#endif + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + /* Clean up and return the status if the command was not successful */ + /* Note: If part of the inquiry, the name is not stored, and the */ + /* inquiry complete callback is called. */ + + if ((hci_status == HCI_SUCCESS)) + { + /* Copy the name from the data stream into the return structure */ + /* Note that even if it is not being returned, it is used as a */ + /* temporary buffer. */ + p_n1 = (UINT8 *)rem_name.remote_bd_name; + rem_name.length = (evt_len < BD_NAME_LEN) ? evt_len : BD_NAME_LEN; + rem_name.status = BTM_SUCCESS; + temp_evt_len = rem_name.length; + + while (temp_evt_len > 0) + { + *p_n1++ = *bdn++; + temp_evt_len--; + } + } + + + /* If processing a stand alone remote name then report the error in the callback */ + else + { + rem_name.status = BTM_BAD_VALUE_RET; + rem_name.length = 0; + rem_name.remote_bd_name[0] = 0; + } + /* Reset the remote BAD to zero and call callback if possible */ + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + p_inq->p_remname_cmpl_cb = NULL; + if (p_cb) + (p_cb)((tBTM_REMOTE_DEV_NAME *)&rem_name); + } + + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + /* If existing entry, update the name */ + if ((bda != NULL) && ((p_i = btm_inq_db_find (bda)) != NULL) + && (hci_status == HCI_SUCCESS)) + { + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_DONE; + p_n = p_i->inq_info.remote_name; + memset(p_n, 0, BTM_MAX_REM_BD_NAME_LEN + 1); + p_i->inq_info.remote_name_len = (rem_name.length < BTM_MAX_REM_BD_NAME_LEN) ? + rem_name.length : BTM_MAX_REM_BD_NAME_LEN; + evt_len = p_i->inq_info.remote_name_len; + p_n1 = (UINT8 *)rem_name.remote_bd_name; + while (evt_len > 0) + { + *p_n++ = *p_n1++; + evt_len--; + } + + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_i->inq_info, TRUE); + } + else + { + if (p_i) + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + { + /* Find the entry which is currently doing name request */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_PENDING) + { + /* Should be only one */ + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + break; + } + } + } + } + + /* If an inquiry is in progress then update other entries */ + if (p_inq->inq_active) + { + /* Check if there are any more entries inquired but not named */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_EMPTY) + { + p_cur->remote_name_state = BTM_INQ_RMT_NAME_PENDING; +#if (BLE_INCLUDED == TRUE) + BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (btm_ble_read_remote_name(remote_bda, p_cur, p_cb) != BTM_CMD_STARTED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + return; + } + else +#endif + { + if (btm_initiate_rem_name (p_cur->results.remote_bd_addr, + p_cur, BTM_RMT_NAME_INQ, + BTM_INQ_RMT_NAME_TIMEOUT, NULL) != BTM_CMD_STARTED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + return; + } + } + } + + /* The inquiry has finished so call the callback for the inquiry */ + p_inq_cb = p_inq->p_inq_cmpl_cb; + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->p_inq_cmpl_cb = NULL; + + /* If we have a callback registered for inquiry complete, call it */ + if (p_inq_cb) + (p_inq_cb)((tBTM_INQUIRY_CMPL *) &p_inq->inq_cmpl_info); + + /* In some cases we can not get name of the device once but will be */ + /* able to do it next time. Until we have better solution we will */ + /* try to get name every time */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_FAILED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } + } +#endif /* BTM_INQ_GET_REMOTE_NAME == TRUE */ +} + +/******************************************************************************* +** +** Function btm_inq_rmt_name_failed +** +** Description This function is if timeout expires while getting remote +** name. This is done for devices that incorrectly do not +** report operation failure +** +** Returns void +** +*******************************************************************************/ +void btm_inq_rmt_name_failed (void) +{ + BTM_TRACE_ERROR1 ("btm_inq_rmt_name_failed() remname_active=%d", btm_cb.btm_inq_vars.remname_active); + + if (btm_cb.btm_inq_vars.remname_active) + btm_process_remote_name (btm_cb.btm_inq_vars.remname_bda, NULL, 0, HCI_ERR_UNSPECIFIED); + else + btm_process_remote_name (NULL, NULL, 0, HCI_ERR_UNSPECIFIED); + + btm_sec_rmt_name_request_complete (NULL, NULL, HCI_ERR_UNSPECIFIED); +} +/******************************************************************************* +** +** Function btm_read_linq_tx_power_complete +** +** Description read inquiry tx power level complete callback function. +** +** Returns void +** +*******************************************************************************/ +void btm_read_linq_tx_power_complete(UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_txpwer_cmpl_cb; + tBTM_INQ_TXPWR_RESULTS results; + + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + /* If there was a callback registered for read inq tx power, call it */ + btm_cb.devcb.p_txpwer_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (results.hci_status, p); + + if (results.hci_status == HCI_SUCCESS) + { + results.status = BTM_SUCCESS; + + STREAM_TO_UINT8 (results.tx_power, p); + BTM_TRACE_EVENT2 ("BTM INQ TX POWER Complete: tx_power %d, hci status 0x%02x", + results.tx_power, results.hci_status); + } + else + results.status = BTM_ERR_PROCESSING; + + (*p_cb)(&results); + } + +} +/******************************************************************************* +** +** Function BTM_WriteEIR +** +** Description This function is called to write EIR data to controller. +** +** Parameters p_buff - allocated HCI command buffer including extended +** inquriry response +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff ) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + if (HCI_EXT_INQ_RSP_SUPPORTED(btm_cb.devcb.local_features)) + { + BTM_TRACE_API0("Write Extended Inquiry Response to controller"); + btsnd_hcic_write_ext_inquiry_response (p_buff, BTM_EIR_DEFAULT_FEC_REQUIRED); + return BTM_SUCCESS; + } + else + { + GKI_freebuf(p_buff); + return BTM_MODE_UNSUPPORTED; + } +#else + GKI_freebuf(p_buff); + return BTM_SUCCESS; +#endif +} + +/******************************************************************************* +** +** Function BTM_CheckEirData +** +** Description This function is called to get EIR data from significant part. +** +** Parameters p_eir - pointer of EIR significant part +** type - finding EIR data type +** p_length - return the length of EIR data not including type +** +** Returns pointer of EIR data +** +*******************************************************************************/ +UINT8 *BTM_CheckEirData( UINT8 *p_eir, UINT8 type, UINT8 *p_length ) +{ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p = p_eir; + UINT8 length; + UINT8 eir_type; + BTM_TRACE_API1("BTM_CheckEirData type=0x%02X", type); + + STREAM_TO_UINT8(length, p); + while( length && (p - p_eir <= HCI_EXT_INQ_RESPONSE_LEN)) + { + STREAM_TO_UINT8(eir_type, p); + if( eir_type == type ) + { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + p += length - 1; /* skip the length of data */ + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +#else + return NULL; +#endif +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_eir_service +** +** Description This function is called to get the bit position of UUID. +** +** Parameters uuid16 - UUID 16-bit +** +** Returns BTM EIR service ID if found +** BTM_EIR_MAX_SERVICES - if not found +** +*******************************************************************************/ +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ) +{ + UINT8 xx; + + for( xx = 0; xx < BTM_EIR_MAX_SERVICES; xx++ ) + { + if( uuid16 == BTM_EIR_UUID_LKUP_TBL[xx]) + { + return xx; + } + } + return BTM_EIR_MAX_SERVICES; +} +#endif + +/******************************************************************************* +** +** Function BTM_HasEirService +** +** Description This function is called to know if UUID in bit map of UUID. +** +** Parameters p_eir_uuid - bit map of UUID list +** uuid16 - UUID 16-bit +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + return( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_id )); + else + return( FALSE ); +#else + return( FALSE ); +#endif +} + +/******************************************************************************* +** +** Function BTM_HasInquiryEirService +** +** Description This function is called to know if UUID in bit map of UUID list. +** +** Parameters p_results - inquiry results +** uuid16 - UUID 16-bit +** +** Returns BTM_EIR_FOUND - if found +** BTM_EIR_NOT_FOUND - if not found and it is complete list +** BTM_EIR_UNKNOWN - if not found and it is not complete list +** +*******************************************************************************/ +tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService( tBTM_INQ_RESULTS *p_results, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + if( BTM_HasEirService( p_results->eir_uuid, uuid16 )) + { + return BTM_EIR_FOUND; + } + else if( p_results->eir_complete_list ) + { + return BTM_EIR_NOT_FOUND; + } + else + return BTM_EIR_UNKNOWN; +#else + return BTM_EIR_UNKNOWN; +#endif +} + +/******************************************************************************* +** +** Function BTM_AddEirService +** +** Description This function is called to add a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_AddEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + BTM_EIR_SET_SERVICE( p_eir_uuid, service_id ); +#endif +} + +/******************************************************************************* +** +** Function BTM_RemoveEirService +** +** Description This function is called to remove a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + BTM_EIR_CLR_SERVICE( p_eir_uuid, service_id ); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetEirSupportedServices +** +** Description This function is called to get UUID list from bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** p - reference of current pointer of EIR +** max_num_uuid16 - max number of UUID can be written in EIR +** num_uuid16 - number of UUID have been written in EIR +** +** Returns BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise +** +*******************************************************************************/ +UINT8 BTM_GetEirSupportedServices( UINT32 *p_eir_uuid, UINT8 **p, + UINT8 max_num_uuid16, UINT8 *p_num_uuid16) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + UINT8 service_index; + + *p_num_uuid16 = 0; + + for(service_index = 0; service_index < BTM_EIR_MAX_SERVICES; service_index++) + { + if( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_index )) + { + if( *p_num_uuid16 < max_num_uuid16 ) + { + UINT16_TO_STREAM(*p, BTM_EIR_UUID_LKUP_TBL[service_index]); + (*p_num_uuid16)++; + } + /* if max number of UUIDs are stored and found one more */ + else + { + return BTM_EIR_MORE_16BITS_UUID_TYPE; + } + } + } + return BTM_EIR_COMPLETE_16BITS_UUID_TYPE; +#else + return BTM_EIR_COMPLETE_16BITS_UUID_TYPE; +#endif +} + +/******************************************************************************* +** +** Function BTM_GetEirUuidList +** +** Description This function parses EIR and returns UUID list. +** +** Parameters p_eir - EIR +** uuid_size - LEN_UUID_16, LEN_UUID_32, LEN_UUID_128 +** p_num_uuid - return number of UUID in found list +** p_uuid_list - return UUID list +** max_num_uuid - maximum number of UUID to be returned +** +** Returns 0 - if not found +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE +** BTM_EIR_MORE_16BITS_UUID_TYPE +** BTM_EIR_COMPLETE_32BITS_UUID_TYPE +** BTM_EIR_MORE_32BITS_UUID_TYPE +** BTM_EIR_COMPLETE_128BITS_UUID_TYPE +** BTM_EIR_MORE_128BITS_UUID_TYPE +** +*******************************************************************************/ +UINT8 BTM_GetEirUuidList( UINT8 *p_eir, UINT8 uuid_size, UINT8 *p_num_uuid, + UINT8 *p_uuid_list, UINT8 max_num_uuid) +{ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p_uuid_data; + UINT8 type; + UINT8 yy, xx; + UINT16 *p_uuid16 = (UINT16 *)p_uuid_list; + UINT32 *p_uuid32 = (UINT32 *)p_uuid_list; + char buff[LEN_UUID_128 * 2 + 1]; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, uuid_size, p_num_uuid, &type ); + if( p_uuid_data == NULL ) + { + return 0x00; + } + + if( *p_num_uuid > max_num_uuid ) + { + BTM_TRACE_WARNING2("BTM_GetEirUuidList number of uuid in EIR = %d, size of uuid list = %d", + *p_num_uuid, max_num_uuid ); + *p_num_uuid = max_num_uuid; + } + + BTM_TRACE_DEBUG2("BTM_GetEirUuidList type = %02X, number of uuid = %d", type, *p_num_uuid ); + + if( uuid_size == LEN_UUID_16 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_UINT16(*(p_uuid16 + yy), p_uuid_data); + BTM_TRACE_DEBUG1(" 0x%04X", *(p_uuid16 + yy)); + } + } + else if( uuid_size == LEN_UUID_32 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_UINT32(*(p_uuid32 + yy), p_uuid_data); + BTM_TRACE_DEBUG1(" 0x%08X", *(p_uuid32 + yy)); + } + } + else if( uuid_size == LEN_UUID_128 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_ARRAY16(p_uuid_list + yy * LEN_UUID_128, p_uuid_data); + for( xx = 0; xx < LEN_UUID_128; xx++ ) + sprintf(buff + xx*2, "%02X", *(p_uuid_list + yy * LEN_UUID_128 + xx)); + BTM_TRACE_DEBUG1(" 0x%s", buff); + } + } + + return type; +#else + *p_num_uuid = 0; + return 0x00; +#endif +} + + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_eir_get_uuid_list +** +** Description This function searches UUID list in EIR. +** +** Parameters p_eir - address of EIR +** uuid_size - size of UUID to find +** p_num_uuid - number of UUIDs found +** p_uuid_list_type - EIR data type +** +** Returns NULL - if UUID list with uuid_size is not found +** beginning of UUID list in EIR - otherwise +** +*******************************************************************************/ +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ) +{ + UINT8 *p_uuid_data; + UINT8 complete_type, more_type; + UINT8 uuid_len; + + switch( uuid_size ) + { + case LEN_UUID_16: + complete_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + break; + case LEN_UUID_32: + complete_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_32BITS_UUID_TYPE; + break; + case LEN_UUID_128: + complete_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_128BITS_UUID_TYPE; + break; + default: + *p_num_uuid = 0; + return NULL; + break; + } + + p_uuid_data = BTM_CheckEirData( p_eir, complete_type, &uuid_len ); + if(p_uuid_data == NULL) + { + p_uuid_data = BTM_CheckEirData( p_eir, more_type, &uuid_len ); + *p_uuid_list_type = more_type; + } + else + { + *p_uuid_list_type = complete_type; + } + + *p_num_uuid = uuid_len / uuid_size; + return p_uuid_data; +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_uuid16 +** +** Description This function converts UUID to UUID 16-bit. +** +** Parameters p_uuid - address of UUID +** uuid_size - size of UUID +** +** Returns 0 - if UUID cannot be converted to UUID 16-bit +** UUID 16-bit - otherwise +** +*******************************************************************************/ +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ) +{ + static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + UINT16 uuid16 = 0; + UINT32 uuid32; + BOOLEAN is_base_uuid; + UINT8 xx; + + switch (uuid_size) + { + case LEN_UUID_16: + STREAM_TO_UINT16 (uuid16, p_uuid); + break; + case LEN_UUID_32: + STREAM_TO_UINT32 (uuid32, p_uuid); + if (uuid32 < 0x10000) + uuid16 = (UINT16) uuid32; + break; + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) + { + if (p_uuid[xx] != base_uuid[xx]) + { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) + { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) + { + p_uuid += (LEN_UUID_128 - 4); + STREAM_TO_UINT16(uuid16, p_uuid); + } + } + break; + default: + BTM_TRACE_WARNING0("btm_convert_uuid_to_uuid16 invalid uuid size"); + break; + } + + return( uuid16); +} + +/******************************************************************************* +** +** Function btm_set_eir_uuid +** +** Description This function is called to store received UUID into inquiry result. +** +** Parameters p_eir - pointer of EIR significant part +** p_results - pointer of inquiry result +** +** Returns None +** +*******************************************************************************/ +void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ) +{ + UINT8 *p_uuid_data; + UINT8 num_uuid; + UINT16 uuid16; + UINT8 yy; + UINT8 type = BTM_EIR_MORE_16BITS_UUID_TYPE; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_16, &num_uuid, &type ); + + if(type == BTM_EIR_COMPLETE_16BITS_UUID_TYPE) + { + p_results->eir_complete_list = TRUE; + } + else + { + p_results->eir_complete_list = FALSE; + } + + BTM_TRACE_API1("btm_set_eir_uuid eir_complete_list=0x%02X", p_results->eir_complete_list); + + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + STREAM_TO_UINT16(uuid16, p_uuid_data); + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_32, &num_uuid, &type ); + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_32 ); + p_uuid_data += LEN_UUID_32; + if( uuid16 ) + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_128, &num_uuid, &type ); + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_128 ); + p_uuid_data += LEN_UUID_128; + if( uuid16 ) + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + BTM_TRACE_DEBUG2("btm_set_eir_uuid eir_uuid=0x%08X %08X", + p_results->eir_uuid[1], p_results->eir_uuid[0] ); +} +#endif + diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h new file mode 100644 index 0000000..e5f4c15 --- /dev/null +++ b/stack/btm/btm_int.h @@ -0,0 +1,1114 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ +#ifndef BTM_INT_H +#define BTM_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" + +#if RFCOMM_INCLUDED == TRUE +#include "rfcdefs.h" +#endif + +#include "btm_api.h" + +#if (BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#if (SMP_INCLUDED == TRUE) +#include "smp_api.h" +#endif +#endif + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 +typedef char tBTM_LOC_BD_NAME[BTM_MAX_LOC_BD_NAME_LEN + 1]; +#endif + +#define BTM_ACL_IS_CONNECTED(bda) (btm_bda_to_acl (bda) != NULL) + +/* Definitions for Server Channel Number (SCN) management +*/ +#define BTM_MAX_SCN PORT_MAX_RFC_PORTS + +/* Define masks for supported and exception 2.0 ACL packet types +*/ +#define BTM_ACL_SUPPORTED_PKTS_MASK (HCI_PKT_TYPES_MASK_DM1 | \ + HCI_PKT_TYPES_MASK_DH1 | \ + HCI_PKT_TYPES_MASK_DM3 | \ + HCI_PKT_TYPES_MASK_DH3 | \ + HCI_PKT_TYPES_MASK_DM5 | \ + HCI_PKT_TYPES_MASK_DH5) + +#define BTM_ACL_EXCEPTION_PKTS_MASK (HCI_PKT_TYPES_MASK_NO_2_DH1 | \ + HCI_PKT_TYPES_MASK_NO_3_DH1 | \ + HCI_PKT_TYPES_MASK_NO_2_DH3 | \ + HCI_PKT_TYPES_MASK_NO_3_DH3 | \ + HCI_PKT_TYPES_MASK_NO_2_DH5 | \ + HCI_PKT_TYPES_MASK_NO_3_DH5) + +#define BTM_EPR_AVAILABLE(p) ((HCI_ATOMIC_ENCRYPT_SUPPORTED((p)->features) && \ + HCI_ATOMIC_ENCRYPT_SUPPORTED(btm_cb.devcb.local_features)) \ + ? TRUE : FALSE) + +#define BTM_IS_BRCM_CONTROLLER() (btm_cb.devcb.local_version.manufacturer == LMP_COMPID_BROADCOM) + + +/* Define the ACL Management control structure +*/ +typedef struct +{ + UINT16 hci_handle; + UINT16 pkt_types_mask; + UINT16 restore_pkt_types; /* when coming in/out of SCO connection restore the packet types */ + UINT16 clock_offset; + BD_ADDR remote_addr; + DEV_CLASS remote_dc; + BD_NAME remote_name; + + UINT16 manufacturer; + UINT16 lmp_subversion; + UINT16 link_super_tout; + BD_FEATURES features; /* Features suported by the device */ + UINT8 lmp_version; + + BOOLEAN in_use; + UINT8 link_role; + BOOLEAN link_up_issued; /* True if busy_level link up has been issued */ + +#define BTM_ACL_SWKEY_STATE_IDLE 0 +#define BTM_ACL_SWKEY_STATE_MODE_CHANGE 1 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF 2 +#define BTM_ACL_SWKEY_STATE_SWITCHING 3 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_ON 4 +#define BTM_ACL_SWKEY_STATE_IN_PROGRESS 5 + UINT8 switch_role_state; + UINT8 change_key_state; + +#define BTM_ACL_ENCRYPT_STATE_IDLE 0 +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF 1 /* encryption turning off */ +#define BTM_ACL_ENCRYPT_STATE_TEMP_FUNC 2 /* temporarily off for change link key or role switch */ +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON 3 /* encryption turning on */ + UINT8 encrypt_state; /* overall BTM encryption state */ + +#if BTM_PWR_MGR_INCLUDED == FALSE + UINT8 mode; +#endif /* BTM_PWR_MGR_INCLUDED */ +#if BLE_INCLUDED == TRUE + UINT8 is_le_link; +#endif + +} tACL_CONN; + +/***************************************************** +** TIMER Definitions +******************************************************/ +#define TT_DEV_RESET 1 +#define TT_DEV_RLN 2 +#define TT_DEV_RLNKP 4 /* Read Link Policy Settings */ + +/* Define the Device Management control structure +*/ +typedef struct +{ + tBTM_DEV_STATUS_CB *p_dev_status_cb; /* Device status change callback */ + tBTM_VS_EVT_CB *p_vend_spec_cb[BTM_MAX_VSE_CALLBACKS]; /* Register for vendor specific events */ + + tBTM_CMPL_CB *p_stored_link_key_cmpl_cb; /* Read/Write/Delete stored link key */ + + TIMER_LIST_ENT reset_timer; + tBTM_CMPL_CB *p_reset_cmpl_cb; /* Callback function to be called */ + /* when startup of the device is done */ + TIMER_LIST_ENT rln_timer; + tBTM_CMPL_CB *p_rln_cmpl_cb; /* Callback function to be called when */ + /* read local name function complete */ + TIMER_LIST_ENT rlinkp_timer; + tBTM_CMPL_CB *p_rlinkp_cmpl_cb; /* Callback function to be called when */ + /* read link policy function completes */ + TIMER_LIST_ENT rssi_timer; + tBTM_CMPL_CB *p_rssi_cmpl_cb; /* Callback function to be called when */ + /* read rssi function completes */ + TIMER_LIST_ENT lnk_quality_timer; + tBTM_CMPL_CB *p_lnk_qual_cmpl_cb;/* Callback function to be called when */ + /* read link quality function completes */ + TIMER_LIST_ENT txpwer_timer; + tBTM_CMPL_CB *p_txpwer_cmpl_cb; /* Callback function to be called when */ + /* read inq tx power function completes */ + + TIMER_LIST_ENT qossu_timer; + tBTM_CMPL_CB *p_qossu_cmpl_cb; /* Callback function to be called when */ + /* qos setup function completes */ + + tBTM_ROLE_SWITCH_CMPL switch_role_ref_data; + tBTM_CMPL_CB *p_switch_role_cb; /* Callback function to be called when */ + /* requested switch role is completed */ + + tBTM_CHANGE_KEY_CMPL chg_link_key_ref_data; + tBTM_CMPL_CB *p_chg_link_key_cb; /* Callback function to be called when */ + /* change of link key is completed */ + + TIMER_LIST_ENT tx_power_timer; + tBTM_CMPL_CB *p_tx_power_cmpl_cb; /* Callback function to be called */ + + BD_ADDR local_addr; /* BD_ADDR of the local device */ + tBTM_VERSION_INFO local_version; /* Local Version Information */ + BD_FEATURES local_features; /* Local features bit mask */ + DEV_CLASS dev_class; /* Local device class */ +#if BLE_INCLUDED == TRUE + BD_ADDR read_tx_pwr_addr; /* read TX power target address */ + + tBTM_BLE_LOCAL_ID_KEYS id_keys; /* local BLE ID keys */ + BT_OCTET16 er; /* BLE encryption key */ + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + BOOLEAN no_disc_if_pair_fail; + BOOLEAN enable_test_mac_val; + BT_OCTET8 test_mac; + BOOLEAN enable_test_local_sign_cntr; + UINT32 test_local_sign_cntr; +#endif + + +#endif /* BLE_INCLUDED */ + +#define BTM_DEV_STATE_WAIT_RESET_CMPLT 0 +#define BTM_DEV_STATE_WAIT_AFTER_RESET 1 +#define BTM_DEV_STATE_READY 2 + + UINT8 state; + tBTM_IO_CAP loc_io_caps; /* IO capability of the local device */ + BOOLEAN loc_auth_req; /* the auth_req flag */ + BD_FEATURES brcm_features; /* Broadcom specific features bit mask */ +} tBTM_DEVCB; + + +/* Define the structures and constants used for inquiry +*/ + +/* Definitions of limits for inquiries */ +#define BTM_PER_INQ_MIN_MAX_PERIOD HCI_PER_INQ_MIN_MAX_PERIOD +#define BTM_PER_INQ_MAX_MAX_PERIOD HCI_PER_INQ_MAX_MAX_PERIOD +#define BTM_PER_INQ_MIN_MIN_PERIOD HCI_PER_INQ_MIN_MIN_PERIOD +#define BTM_PER_INQ_MAX_MIN_PERIOD HCI_PER_INQ_MAX_MIN_PERIOD +#define BTM_MAX_INQUIRY_LENGTH HCI_MAX_INQUIRY_LENGTH +#define BTM_MIN_INQUIRY_LEN 0x01 + +#define BTM_MIN_INQ_TX_POWER -70 +#define BTM_MAX_INQ_TX_POWER 20 + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE +typedef struct +{ + UINT32 inq_count; /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + BD_ADDR bd_addr; +} tINQ_BDADDR; +#endif + +typedef struct +{ + UINT32 time_of_resp; + UINT32 inq_count; /* "timestamps" the entry with a particular inquiry count */ + /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + tBTM_INQ_INFO inq_info; + BOOLEAN in_use; + +#if (BLE_INCLUDED == TRUE) + BOOLEAN scan_rsp; +#endif +} tINQ_DB_ENT; + + +typedef struct +{ + tBTM_CMPL_CB *p_remname_cmpl_cb; + +#define BTM_EXT_RMT_NAME_TIMEOUT 40 + + + TIMER_LIST_ENT rmt_name_timer_ent; + + UINT16 discoverable_mode; + UINT16 connectable_mode; + UINT16 page_scan_window; + UINT16 page_scan_period; + UINT16 inq_scan_window; + UINT16 inq_scan_period; + UINT16 inq_scan_type; + UINT16 page_scan_type; /* current page scan type */ + + BD_ADDR remname_bda; /* Name of bd addr for active remote name request */ +#define BTM_RMT_NAME_INACTIVE 0 +#define BTM_RMT_NAME_EXT 0x1 /* Initiated through API */ +#define BTM_RMT_NAME_SEC 0x2 /* Initiated internally by security manager */ +#define BTM_RMT_NAME_INQ 0x4 /* Remote name initiated internally by inquiry */ + BOOLEAN remname_active; /* State of a remote name request by external API */ + + tBTM_CMPL_CB *p_inq_cmpl_cb; + tBTM_INQ_RESULTS_CB *p_inq_results_cb; + tBTM_CMPL_CB *p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry filter completed */ + tBTM_INQ_DB_CHANGE_CB *p_inq_change_cb; /* Inquiry database changed callback */ + UINT32 inq_counter; /* Counter incremented each time an inquiry completes */ + /* Used for determining whether or not duplicate devices */ + /* have responded to the same inquiry */ + TIMER_LIST_ENT inq_timer_ent; +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tINQ_BDADDR *p_bd_db; /* Pointer to memory that holds bdaddrs */ + UINT16 num_bd_entries; /* Number of entries in database */ + UINT16 max_bd_entries; /* Maximum number of entries that can be stored */ +#endif + tINQ_DB_ENT inq_db[BTM_INQ_DB_SIZE]; + tBTM_INQ_PARMS inqparms; /* Contains the parameters for the current inquiry */ + tBTM_INQUIRY_CMPL inq_cmpl_info; /* Status and number of responses from the last inquiry */ + + UINT16 per_min_delay; /* Current periodic minimum delay */ + UINT16 per_max_delay; /* Current periodic maximum delay */ + BOOLEAN inqfilt_active; + UINT8 pending_filt_complete_event; /* to take care of btm_event_filter_complete corresponding to */ + /* inquiry that has been cancelled*/ + UINT8 inqfilt_type; /* Contains the inquiry filter type (BD ADDR, COD, or Clear) */ + +#define BTM_INQ_INACTIVE_STATE 0 +#define BTM_INQ_CLR_FILT_STATE 1 /* Currently clearing the inquiry filter preceeding the inquiry request */ + /* (bypassed if filtering is not used) */ +#define BTM_INQ_SET_FILT_STATE 2 /* Sets the new filter (or turns off filtering) in this state */ +#define BTM_INQ_ACTIVE_STATE 3 /* Actual inquiry or periodic inquiry is in progress */ +#define BTM_INQ_REMNAME_STATE 4 /* Remote name requests are active */ + + UINT8 state; /* Current state that the inquiry process is in */ + UINT8 inq_active; /* Bit Mask indicating type of inquiry is active */ + BOOLEAN no_inc_ssp; /* TRUE, to stop inquiry on incoming SSP */ +} tBTM_INQUIRY_VAR_ST; + +/* The MSB of the clock offset field indicates that the offset is valid if TRUE */ +#define BTM_CLOCK_OFFSET_VALID 0x8000 + +/* Define the structures needed by security management +*/ + +#define BTM_SEC_INVALID_HANDLE 0xFFFF + +typedef UINT8 *BTM_BD_NAME_PTR; /* Pointer to Device name */ + +/* Security callback is called by this unit when security +** procedures are completed. Parameters are +** BD Address of remote +** Result of the operation +*/ +typedef tBTM_SEC_CBACK tBTM_SEC_CALLBACK; + +typedef void (tBTM_SCO_IND_CBACK) (UINT16 sco_inx) ; + +/* MACROs to convert from SCO packet types mask to ESCO and back */ +#define BTM_SCO_PKT_TYPE_MASK ( HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3) + +/* Mask defining only the SCO types of an esco packet type */ +#define BTM_ESCO_PKT_TYPE_MASK ( HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3) + +#define BTM_SCO_2_ESCO(scotype) ((UINT16)(((scotype) & BTM_SCO_PKT_TYPE_MASK) >> 5)) +#define BTM_ESCO_2_SCO(escotype) ((UINT16)(((escotype) & BTM_ESCO_PKT_TYPE_MASK) << 5)) + +/* Define masks for supported and exception 2.0 SCO packet types +*/ +#define BTM_SCO_SUPPORTED_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_HV1 | \ + HCI_ESCO_PKT_TYPES_MASK_HV2 | \ + HCI_ESCO_PKT_TYPES_MASK_HV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV4 | \ + HCI_ESCO_PKT_TYPES_MASK_EV5) + +#define BTM_SCO_EXCEPTION_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5) + + +#define BTM_SCO_ROUTE_UNKNOWN 0xff + +/* Define the structure that contains (e)SCO data */ +typedef struct +{ + tBTM_ESCO_CBACK *p_esco_cback; /* Callback for eSCO events */ + tBTM_ESCO_PARAMS setup; + tBTM_ESCO_DATA data; /* Connection complete information */ + UINT8 hci_status; +} tBTM_ESCO_INFO; + +/* Define the structure used for SCO Management +*/ +typedef struct +{ + tBTM_ESCO_INFO esco; /* Current settings */ +#if BTM_SCO_HCI_INCLUDED == TRUE + BUFFER_Q xmit_data_q; /* SCO data transmitting queue */ +#endif + tBTM_SCO_CB *p_conn_cb; /* Callback for when connected */ + tBTM_SCO_CB *p_disc_cb; /* Callback for when disconnect */ + UINT16 state; /* The state of the SCO link */ + UINT16 hci_handle; /* HCI Handle */ + BOOLEAN is_orig; /* TRUE if the originator */ + BOOLEAN rem_bd_known; /* TRUE if remote BD addr known */ + +} tSCO_CONN; + +/* SCO Management control block */ +typedef struct +{ + tBTM_SCO_IND_CBACK *app_sco_ind_cb; +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_DATA_CB *p_data_cb; /* Callback for SCO data over HCI */ + UINT32 xmit_window_size; /* Total SCO window in bytes */ +#endif + tSCO_CONN sco_db[BTM_MAX_SCO_LINKS]; + tBTM_ESCO_PARAMS def_esco_parms; + BD_ADDR xfer_addr; + UINT16 sco_disc_reason; + BOOLEAN esco_supported; /* TRUE if 1.2 cntlr AND supports eSCO links */ + tBTM_SCO_TYPE desired_sco_mode; + tBTM_SCO_TYPE xfer_sco_type; + tBTM_SCO_PCM_PARAM sco_pcm_param; + tBTM_SCO_CODEC_TYPE codec_in_use; /* None, CVSD, MSBC, etc. */ +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_ROUTE_TYPE sco_path; +#endif + +} tSCO_CB; + + +#if BTM_SCO_INCLUDED == TRUE +extern void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ); +extern void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb); +extern void btm_reject_sco_link(UINT16 sco_inx ); +extern void btm_sco_chk_pend_rolechange (UINT16 hci_handle); +#else +#define btm_accept_sco_link(sco_inx, p_setup, p_conn_cb, p_disc_cb) +#define btm_reject_sco_link(sco_inx) +#define btm_set_sco_ind_cback(sco_ind_cb) +#define btm_sco_chk_pend_rolechange(hci_handle) +#endif /* BTM_SCO_INCLUDED */ + +/* +** Define structure for Security Service Record. +** A record exists for each service registered with the Security Manager +*/ +typedef struct +{ + UINT32 mx_proto_id; /* Service runs over this multiplexer protocol */ + UINT32 orig_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT32 term_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT16 psm; /* L2CAP PSM value */ + UINT16 security_flags; /* Bitmap of required security features */ + UINT8 service_id; /* Passed in authorization callback */ +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT16 ucd_security_flags; /* Bitmap of required security features for UCD */ +#endif +#if BTM_SEC_SERVICE_NAME_LEN > 0 + UINT8 orig_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; + UINT8 term_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; +#endif +} tBTM_SEC_SERV_REC; + +#if BLE_INCLUDED == TRUE +/* LE Security information of device in Slave Role */ +typedef struct +{ + BT_OCTET16 irk; /* peer diverified identity root */ + BT_OCTET16 ltk; /* peer long term key */ + BT_OCTET16 csrk; /* peer SRK peer device used to secured sign local data */ + + BT_OCTET8 rand; /* random vector for LTK generation */ + UINT16 ediv; /* LTK diversifier of this slave device */ + UINT16 div; /* local DIV to generate local LTK=d1(ER,DIV,0) and CSRK=d1(ER,DIV,1) */ + UINT8 sec_level; /* local pairing security level */ + UINT8 key_size; /* key size of the LTK delivered to peer device */ + UINT8 srk_sec_level; /* security property of peer SRK for this device */ + UINT8 local_csrk_sec_level; /* security property of local CSRK for this device */ + + UINT32 counter; /* peer sign counter for verifying rcv signed cmd */ + UINT32 local_counter; /* local sign counter for sending signed write cmd*/ +}tBTM_SEC_BLE_KEYS; + +#endif /* BLE_INCLUDED */ + +typedef struct +{ + tBLE_ADDR_TYPE ble_addr_type; /* LE device type: public or random address */ + BD_ADDR reconn_addr; /* reconnect address */ + BD_ADDR cur_rand_addr; /* current random address */ + BD_ADDR static_addr; /* static address */ + +#if SMP_INCLUDED == TRUE + tBTM_LE_KEY_TYPE key_type; /* bit mask of valid key types in record */ + tBTM_SEC_BLE_KEYS keys; /* LE device security info in slave rode */ +#endif +} tBTM_SEC_BLE; + +/* +** Define structure for Security Device Record. +** A record exists for each device authenticated with this device +*/ +typedef struct +{ + tBTM_SEC_SERV_REC *p_cur_service; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 timestamp; /* Timestamp of the last connection */ + UINT32 trusted_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; /* Bitwise OR of trusted services */ + UINT16 hci_handle; /* Handle to connection when exists */ + UINT16 clock_offset; /* Latest known clock offset */ + BD_ADDR bd_addr; /* BD_ADDR of the device */ + DEV_CLASS dev_class; /* DEV_CLASS of the device */ + LINK_KEY link_key; /* Device link key */ + +#define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED +#define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED +#define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED +#define BTM_SEC_NAME_KNOWN 0x08 +#define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN +#define BTM_SEC_LINK_KEY_AUTHED 0x20 +#define BTM_SEC_ROLE_SWITCHED 0x40 +#define BTM_SEC_IN_USE 0x80 + + tBTM_BD_NAME sec_bd_name; /* User friendly name of the device. (may be truncated to save space in dev_rec table) */ + UINT8 sec_flags; /* Current device security state */ + BD_FEATURES features; /* Features suported by the device */ + +#define BTM_SEC_STATE_IDLE 0 +#define BTM_SEC_STATE_AUTHENTICATING 1 +#define BTM_SEC_STATE_ENCRYPTING 2 +#define BTM_SEC_STATE_GETTING_NAME 3 +#define BTM_SEC_STATE_AUTHORIZING 4 +#define BTM_SEC_STATE_SWITCHING_ROLE 5 +#define BTM_SEC_STATE_DISCONNECTING 6 +#define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around controller problems */ + + UINT8 sec_state; /* Operating state */ + BOOLEAN is_originator; /* TRUE if device is originating connection */ +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; /* TRUE if device is sending or receiving UCD */ + /* if incoming security failed, received UCD will be discarded */ +#endif + BOOLEAN role_master; /* TRUE if current mode is master */ + UINT16 security_required; /* Security required for connection */ + BOOLEAN link_key_not_sent; /* link key notification has not been sent waiting for name */ + UINT8 link_key_type; /* Type of key used in pairing */ + BOOLEAN link_key_changed; /* Changed link key during current connection */ + +#define BTM_SM4_UNKNOWN 0x00 +#define BTM_SM4_KNOWN 0x10 +#define BTM_SM4_TRUE 0x11 +#define BTM_SM4_REQ_PEND 0x08 /* set this bit when getting remote features */ +#define BTM_SM4_UPGRADE 0x04 /* set this bit when upgrading link key */ +#define BTM_SM4_RETRY 0x02 /* set this bit to retry on HCI_ERR_KEY_MISSING or HCI_ERR_LMP_ERR_TRANS_COLLISION */ +#define BTM_SM4_DD_ACP 0x20 /* set this bit to indicate peer initiated dedicated bonding */ +#define BTM_SM4_CONN_PEND 0x40 /* set this bit to indicate accepting acl conn; to be cleared on btm_acl_created */ + UINT8 sm4; /* BTM_SM4_TRUE, if the peer supports SM4 */ + tBTM_IO_CAP rmt_io_caps; /* IO capability of the peer device */ + BOOLEAN rmt_auth_req; /* the auth_req flag as in the IO caps rsp evt */ + +#if (BLE_INCLUDED == TRUE) + UINT8 enc_key_size; /* current link encryption key size */ + tBTM_SEC_BLE ble; + tBT_DEVICE_TYPE device_type; + tBTM_LE_CONN_PRAMS conn_params; +#endif + +// btla-specific ++ +#if BTM_DISC_DURING_RS == TRUE +#define BTM_SEC_RS_NOT_PENDING 0 /* Role Switch not in progress */ +#define BTM_SEC_RS_PENDING 1 /* Role Switch in progress */ +#define BTM_SEC_DISC_PENDING 2 /* Disconnect is pending */ + UINT8 rs_disc_pending; +#endif +// btla-specific -- +} tBTM_SEC_DEV_REC; + +#define BTM_SEC_IS_SM4(sm) ((BOOLEAN)(BTM_SM4_TRUE == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_LEGACY(sm) ((BOOLEAN)(BTM_SM4_KNOWN == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_UNKNOWN(sm) ((BOOLEAN)(BTM_SM4_UNKNOWN == ((sm)&BTM_SM4_TRUE))) + + +/* +** Define device configuration structure +*/ +typedef struct +{ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + tBTM_LOC_BD_NAME bd_name; /* local Bluetooth device name */ +#endif + BOOLEAN pin_type; /* TRUE if PIN type is fixed */ + UINT8 pin_code_len; /* Bonding information */ + PIN_CODE pin_code; /* PIN CODE if pin type is fixed */ + BOOLEAN connectable; /* If TRUE page scan should be enabled */ + UINT8 def_inq_scan_mode; /* ??? limited/general/none */ +} tBTM_CFG; + +#if BTM_PWR_MGR_INCLUDED == TRUE +enum +{ + BTM_PM_ST_ACTIVE = BTM_PM_STS_ACTIVE, + BTM_PM_ST_HOLD = BTM_PM_STS_HOLD, + BTM_PM_ST_SNIFF = BTM_PM_STS_SNIFF, + BTM_PM_ST_PARK = BTM_PM_STS_PARK, + BTM_PM_ST_PENDING = BTM_PM_STS_PENDING +}; +typedef UINT8 tBTM_PM_STATE; + +enum +{ + BTM_PM_SET_MODE_EVT, /* Set power mode API is called. */ + BTM_PM_UPDATE_EVT, + BTM_PM_RD_MODE_EVT /* Read power mode API is called. */ +}; +typedef UINT8 tBTM_PM_EVENT; + +typedef struct +{ + UINT16 event; + UINT16 len; + UINT8 link_ind; +} tBTM_PM_MSG_DATA; + +typedef struct +{ + UINT8 hci_status; + UINT8 mode; + UINT16 interval; +} tBTM_PM_MD_CHG_DATA; + +typedef struct +{ + UINT8 pm_id; /* the entity that calls SetPowerMode API */ + tBTM_PM_PWR_MD *p_pmd; +} tBTM_PM_SET_MD_DATA; + +typedef struct +{ + void *p_data; + UINT8 link_ind; +} tBTM_PM_SM_DATA; + +typedef struct +{ + tBTM_PM_PWR_MD req_mode[BTM_MAX_PM_RECORDS+1]; /* the desired mode and parameters of the connection*/ + tBTM_PM_PWR_MD set_mode; /* the mode and parameters sent down to the host controller. */ + UINT16 interval; /* the interval from last mode change event. */ +#if (BTM_SSR_INCLUDED == TRUE) + UINT16 max_lat; /* stored SSR maximum latency */ + UINT16 min_rmt_to;/* stored SSR minimum remote timeout */ + UINT16 min_loc_to;/* stored SSR minimum local timeout */ +#endif + tBTM_PM_STATE state; /* contains the current mode of the connection */ + BOOLEAN chg_ind; /* a request change indication */ +} tBTM_PM_MCB; + +#define BTM_PM_REC_NOT_USED 0 +typedef struct +{ + tBTM_PM_STATUS_CBACK *cback;/* to notify the registered party of mode change event */ + UINT8 mask; /* registered request mask. 0, if this entry is not used */ +} tBTM_PM_RCB; +#endif /* BTM_PWR_MGR_INCLUDED */ + +enum +{ + BTM_BLI_ACL_UP_EVT, + BTM_BLI_ACL_DOWN_EVT, + BTM_BLI_PAGE_EVT, + BTM_BLI_PAGE_DONE_EVT, + BTM_BLI_INQ_EVT, + BTM_BLI_INQ_CANCEL_EVT, + BTM_BLI_INQ_DONE_EVT +}; +typedef UINT8 tBTM_BLI_EVENT; + +/* Pairing State */ +enum +{ + BTM_PAIR_STATE_IDLE, /* Idle */ + BTM_PAIR_STATE_GET_REM_NAME, /* Getting the remote name (to check for SM4) */ + BTM_PAIR_STATE_WAIT_PIN_REQ, /* Started authentication, waiting for PIN req (PIN is pre-fetched) */ + BTM_PAIR_STATE_WAIT_LOCAL_PIN, /* Waiting for local PIN code */ + BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, /* Waiting user 'yes' to numeric confirmation */ + BTM_PAIR_STATE_KEY_ENTRY, /* Key entry state (we are a keyboard) */ + BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, /* Waiting for local response to peer OOB data */ + BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, /* Waiting for local IO capabilities and OOB data */ + BTM_PAIR_STATE_INCOMING_SSP, /* Incoming SSP (got peer IO caps when idle) */ + BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, /* All done, waiting authentication cpmplete */ + BTM_PAIR_STATE_WAIT_DISCONNECT /* Waiting to disconnect the ACL */ +}; +typedef UINT8 tBTM_PAIRING_STATE; + +#define BTM_PAIR_FLAGS_WE_STARTED_DD 0x01 /* We want to do dedicated bonding */ +#define BTM_PAIR_FLAGS_PEER_STARTED_DD 0x02 /* Peer initiated dedicated bonding */ +#define BTM_PAIR_FLAGS_DISC_WHEN_DONE 0x04 +#define BTM_PAIR_FLAGS_PIN_REQD 0x08 /* set this bit when pin_callback is called */ +#define BTM_PAIR_FLAGS_PRE_FETCH_PIN 0x10 /* set this bit when pre-fetch pin */ +#define BTM_PAIR_FLAGS_REJECTED_CONNECT 0x20 /* set this bit when rejected incoming connection */ + +typedef struct +{ + BOOLEAN is_mux; + BD_ADDR bd_addr; + UINT16 psm; + BOOLEAN is_orig; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 mx_proto_id; + UINT32 mx_chan_id; +} tBTM_SEC_QUEUE_ENTRY; + +#if (L2CAP_UCD_INCLUDED == TRUE) + +#define CONN_ORIENT_TERM 0x00 /* incoming connection oriented */ +#define CONN_ORIENT_ORIG 0x01 /* outgoing connection oriented */ +#define CONNLESS_TERM 0x02 /* incoming connectionless */ +#define CONNLESS_ORIG 0x03 /* outgoing connectionless */ +#define CONNECTION_TYPE_ORIG_MASK 0x01 /* mask for direction */ +#define CONNECTION_TYPE_CONNLESS_MASK 0x02 /* mask for connectionless or not */ +typedef UINT8 CONNECTION_TYPE; + +#else + +#define CONN_ORIENT_TERM FALSE +#define CONN_ORIENT_ORIG TRUE +typedef BOOLEAN CONNECTION_TYPE; + +#endif /* (L2CAP_UCD_INCLUDED == TRUE) */ + +/* Define a structure to hold all the BTM data +*/ + +#define BTM_STATE_BUFFER_SIZE 5 /* size of state buffer */ + +#if (BTM_PCM2_INCLUDED == TRUE) +/* Define pcm2_action */ +enum +{ + BTM_PCM2_ACT_NONE, + BTM_PCM2_ACT_SENT_ARC, + BTM_PCM2_READ_PARAM, + BTM_PCM2_WRITE_PARAM, +}; +typedef UINT8 tBTM_PCM2_ACTION; +#endif + +typedef struct +{ + tBTM_CFG cfg; /* Device configuration */ + + /**************************************************** + ** ACL Management + ****************************************************/ + tACL_CONN acl_db[MAX_L2CAP_LINKS]; +#if( RFCOMM_INCLUDED==TRUE) + UINT8 btm_scn[BTM_MAX_SCN]; /* current SCNs: TRUE if SCN is in use */ +#endif + UINT16 btm_def_link_policy; + UINT16 btm_def_link_super_tout; + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_EVENT_MASK bl_evt_mask; + tBTM_BL_CHANGE_CB *p_bl_changed_cb; /* Callback for when Busy Level changed */ +#else + tBTM_ACL_DB_CHANGE_CB *p_acl_changed_cb; /* Callback for when ACL DB changed */ +#endif + + tBTM_LSTO_CBACK *p_lsto_cback; /* for link supervision timeout change event */ + + /**************************************************** + ** Power Management + ****************************************************/ +#if BTM_PWR_MGR_INCLUDED == TRUE + tBTM_PM_MCB pm_mode_db[MAX_L2CAP_LINKS]; /* per ACL link */ + tBTM_PM_RCB pm_reg_db[BTM_MAX_PM_RECORDS+1]; /* per application/module */ + UINT8 pm_pend_link; /* the index of acl_db, which has a pending PM cmd */ + UINT8 pm_pend_id; /* the id pf the module, which has a pending PM cmd */ +#endif /* BTM_PWR_MGR_INCLUDED == TRUE */ + + /***************************************************** + ** Device control + *****************************************************/ + tBTM_DEVCB devcb; + + /***************************************************** + ** BLE Device controllers + *****************************************************/ +#if (BLE_INCLUDED == TRUE) + tBTM_BLE_CB ble_ctr_cb; + + UINT16 enc_handle; + BT_OCTET8 enc_rand; /* received rand value from LTK request*/ + UINT16 ediv; /* received ediv value from LTK request */ + UINT8 key_size; +#endif + + /* Packet types supported by the local device */ + UINT16 btm_acl_pkt_types_supported; + UINT16 btm_sco_pkt_types_supported; + + + /***************************************************** + ** Inquiry + *****************************************************/ + tBTM_INQUIRY_VAR_ST btm_inq_vars; + tBTM_FILTER_CB *p_inq_filter_cb; /* Callback that can be set if host */ + /* wants to verify inquiry filters */ + + /***************************************************** + ** SCO Management + *****************************************************/ +#if BTM_SCO_INCLUDED == TRUE + tSCO_CB sco_cb; +#endif + + /***************************************************** + ** Security Management + *****************************************************/ + tBTM_APPL_INFO api; + +#define BTM_SEC_MAX_RMT_NAME_CALLBACKS 2 + tBTM_RMT_NAME_CALLBACK *p_rmt_name_callback[BTM_SEC_MAX_RMT_NAME_CALLBACKS]; + + tBTM_FILTER_CB *p_conn_filter_cb; /* Callback that can be set if host */ + /* wants to verify connectability filters*/ + + tBTM_SEC_DEV_REC *p_collided_dev_rec; + TIMER_LIST_ENT sec_collision_tle; + UINT32 collision_start_time; + UINT32 max_collision_delay; + UINT32 dev_rec_count; /* Counter used for device record timestamp */ + UINT8 security_mode; + BOOLEAN pairing_disabled; + BOOLEAN connect_only_paired; + BOOLEAN security_mode_changed; /* mode changed during bonding */ + BOOLEAN pin_type_changed; /* pin type changed during bonding */ + BOOLEAN sec_req_pending; /* TRUE if a request is pending */ +// btla-specific ++ +#ifdef PORCHE_PAIRING_CONFLICT + UINT8 pin_code_len_saved; /* for legacy devices */ +#endif +// btla-specific -- + + UINT8 pin_code_len; /* for legacy devices */ + PIN_CODE pin_code; /* for legacy devices */ + tBTM_PAIRING_STATE pairing_state; /* The current pairing state */ + UINT8 pairing_flags; /* The current pairing flags */ + BD_ADDR pairing_bda; /* The device currently pairing */ + TIMER_LIST_ENT pairing_tle; /* Timer for pairing process */ + UINT16 disc_handle; /* for legacy devices */ + UINT8 disc_reason; /* for legacy devices */ + tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS]; + tBTM_SEC_DEV_REC sec_dev_rec[BTM_SEC_MAX_DEVICE_RECORDS]; + tBTM_SEC_SERV_REC *p_out_serv; + tBTM_MKEY_CALLBACK *mkey_cback; + + BD_ADDR connecting_bda; + DEV_CLASS connecting_dc; + + UINT8 first_disabled_channel; + UINT8 last_disabled_channel; + + UINT8 acl_disc_reason; + UINT8 trace_level; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + UINT8 num_acl; /* num of active ACL links */ + UINT8 busy_level; /* the current busy level */ + BOOLEAN is_paging; /* TRUE, if paging is in progess */ + BOOLEAN is_inquiry; /* TRUE, if inquiry is in progess */ +#endif + BUFFER_Q page_queue; + BOOLEAN paging; + BOOLEAN discing; + BUFFER_Q sec_pending_q; /* pending sequrity requests in tBTM_SEC_QUEUE_ENTRY format */ + +#if (!defined(BT_TRACE_VERBOSE) || (BT_TRACE_VERBOSE == FALSE)) + char state_temp_buffer[BTM_STATE_BUFFER_SIZE]; +#endif + +#if (defined(BTM_PCM2_INCLUDED) && BTM_PCM2_INCLUDED == TRUE) + UINT16 sys_features; + UINT8 pcm2_params[BRCM_PCM2_SETUP_WRITE_SIZE]; + tBTM_PCM2_ACTION pcm2_action; +#endif + +} tBTM_CB; + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if BTM_DYNAMIC_MEMORY == FALSE +BTM_API extern tBTM_CB btm_cb; +#else +BTM_API extern tBTM_CB *btm_cb_ptr; +#define btm_cb (*btm_cb_ptr) +#endif + +/* Internal functions provided by btm_main.c +******************************************** +*/ +extern void btm_init (void); + +/* Internal functions provided by btm_inq.c +******************************************* +*/ +extern tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, + tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, + tBTM_CMPL_CB *p_cb); + +extern void btm_process_remote_name (BD_ADDR bda, BD_NAME name, UINT16 evt_len, + UINT8 hci_status); +extern void btm_inq_rmt_name_failed(void); + +/* Inquiry related functions */ +extern void btm_clr_inq_db (BD_ADDR p_bda); +extern void btm_inq_db_init (void); +extern void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode); +extern void btm_process_inq_complete (UINT8 status, UINT8 mode); +extern void btm_event_filter_complete (UINT8 *p); +extern void btm_inq_stop_on_ssp(void); +extern void btm_inq_clear_ssp(void); +extern tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda); +extern BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda); + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +extern BOOLEAN btm_lookup_eir(BD_ADDR_PTR p_rem_addr); +#endif + +/* Internal functions provided by btm_acl.c +******************************************** +*/ +extern void btm_acl_init (void); +extern void btm_acl_timeout (TIMER_LIST_ENT *p_tle); +extern void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn, + UINT16 hci_handle, UINT8 link_role, UINT8 is_le_link); +extern void btm_acl_removed (BD_ADDR bda); +extern void btm_acl_device_down (void); +extern void btm_acl_update_busy_level (tBTM_BLI_EVENT event); +extern void btm_acl_link_key_change (UINT16 handle, UINT8 status); + +extern void btm_cont_rswitch_or_chglinkkey (tACL_CONN *p, + tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status); + +extern UINT8 btm_handle_to_acl_index (UINT16 hci_handle); +extern void btm_read_link_policy_complete (UINT8 *p); +extern void btm_read_rssi_complete (UINT8 *p); +extern void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble); +extern void btm_read_link_quality_complete (UINT8 *p); +extern tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types); +extern void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset); +extern void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role); +extern void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +BTM_API extern UINT16 btm_get_acl_disc_reason_code (void); +BTM_API extern tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr); +extern void btm_read_remote_features_complete (UINT8 *p); +extern void btm_read_remote_ext_features_complete (UINT8 *p); +extern void btm_read_remote_ext_features_failed (UINT8 status); +extern void btm_read_remote_version_complete (UINT8 *p); +// btla-specific ++ +extern void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type); +// btla-specific -- +/* Read maximum data packet that can be sent over current connection */ +extern UINT16 btm_get_max_packet_size (BD_ADDR addr); +extern tACL_CONN *btm_bda_to_acl (BD_ADDR bda); +extern BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda); + +#if BTM_PWR_MGR_INCLUDED == FALSE +extern void btm_process_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); + +/* Internal functions provided by btm_pm.c +******************************************** +*/ +#else +extern void btm_pm_reset(void); +extern void btm_pm_sm_alloc(UINT8 ind); +extern void btm_pm_proc_cmd_status(UINT8 status); +extern void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); +extern void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len); +#if BTM_SCO_INCLUDED == TRUE +extern void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle); +#else +#define btm_sco_chk_pend_unpark(hci_status, hci_handle) +#endif /* BTM_SCO_INCLUDED */ +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ +extern void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow); + + +/* Internal functions provided by btm_sco.c +******************************************** +*/ +extern void btm_sco_init (void); +extern void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data); +extern void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len); +extern void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type); +extern void btm_sco_removed (UINT16 hci_handle, UINT8 reason); +extern void btm_sco_acl_removed (BD_ADDR bda); +extern void btm_route_sco_data (BT_HDR *p_msg); +extern BOOLEAN btm_is_sco_active (UINT16 handle); +extern void btm_remove_sco_links (BD_ADDR bda); +extern BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda); + +extern tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms); +extern UINT16 btm_find_scb_by_handle (UINT16 handle); +extern void btm_sco_flush_sco_data(UINT16 sco_inx); + +/* Internal functions provided by btm_devctl.c +********************************************** +*/ +extern void btm_dev_init (void); +extern void btm_dev_absent (void); +extern void btm_dev_timeout (TIMER_LIST_ENT *p_tle); +extern void btm_reset_complete (void); +extern void btm_read_local_version_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_hci_buf_size_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_features_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_addr_complete (UINT8 *p, UINT16 evt_len); +extern void btm_get_local_features (void); + +#if (BLE_INCLUDED == TRUE) +extern void btm_read_ble_buf_size_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_add_2_white_list_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +#endif /* BLE_INCLUDED */ + +/* Vendor Specific Command complete evt handler */ +extern void btm_vsc_complete (UINT8 *p, UINT16 cc_opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback); +extern void btm_inq_db_reset (void); +extern void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len); +extern UINT8 btm_get_hci_version (void); +extern void btm_read_stored_link_key_complete (UINT8 *p); +extern void btm_write_stored_link_key_complete (UINT8 *p); +extern void btm_delete_stored_link_key_complete (UINT8 *p); +extern void btm_return_link_keys_evt (tBTM_RETURN_LINK_KEYS_EVT *result); +extern void btm_report_device_status (tBTM_DEV_STATUS status); + + +/* Internal functions provided by btm_dev.c +********************************************** +*/ +extern BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +extern UINT8 btm_get_voice_coding_support (void); + +extern tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr); +extern void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec); +extern tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr); +extern tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr); +extern tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle); + +/* Internal functions provided by btm_sec.c +********************************************** +*/ +extern BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +extern tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, + UINT16 handle, CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +extern tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +extern void btm_sec_conn_req (UINT8 *bda, UINT8 *dc); +extern void btm_create_conn_cancel_complete (UINT8 *p); +extern void btm_proc_lsto_evt(UINT16 handle, UINT16 timeout); +extern void btm_read_linq_tx_power_complete (UINT8 *p); + +extern void btm_sec_init (UINT8 sec_mode); +extern void btm_sec_dev_reset (void); +extern void btm_sec_abort_access_req (BD_ADDR bd_addr); +extern void btm_sec_auth_complete (UINT16 handle, UINT8 status); +extern void btm_sec_mkey_comp_event (UINT16 handle, UINT8 status, UINT8 key_flg); +extern void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +extern void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode); +extern tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason); +extern void btm_sec_disconnected (UINT16 handle, UINT8 reason); +extern void btm_sec_rmt_name_request_complete (UINT8 *bd_addr, UINT8 *bd_name, UINT8 status); +extern void btm_sec_rmt_host_support_feat_evt (UINT8 *p); +extern void btm_io_capabilities_req (UINT8 *p); +extern void btm_io_capabilities_rsp (UINT8 *p); +extern void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p); +extern void btm_keypress_notif_evt (UINT8 *p); +extern void btm_simple_pair_complete (UINT8 *p); +extern void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type); +extern void btm_sec_link_key_request (UINT8 *p_bda); +extern void btm_sec_pin_code_request (UINT8 *p_bda); +extern void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset); +extern void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res); + +#if BLE_INCLUDED == TRUE +extern void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec); +extern BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_DEV_REC *p_rec); +extern BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda); +#endif /* BLE_INCLUDED */ + +extern tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); + +#if BTM_OOB_INCLUDED == TRUE +extern void btm_rem_oob_req (UINT8 *p); +extern void btm_read_local_oob_complete (UINT8 *p); +#else +#define btm_rem_oob_req(p) +#define btm_read_local_oob_complete(p) +#endif + +extern void btm_acl_resubmit_page (void); +extern void btm_acl_reset_paging (void); +extern void btm_acl_paging (BT_HDR *p, BD_ADDR dest); +extern void btm_acl_set_discing (BOOLEAN discing); +extern UINT8 btm_sec_clr_service_by_psm (UINT16 psm); + +#ifdef BRCM_VS +extern void btm_brcm_feat_init(void); +extern void btm_vs_reset_complete (void); +extern void btm_brcm_arc_init (void); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/btm/btm_main.c b/stack/btm/btm_main.c new file mode 100644 index 0000000..f3037f6 --- /dev/null +++ b/stack/btm/btm_main.c @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the definition of the btm control block when + * BTM_DYNAMIC_MEMORY is used. + * + ******************************************************************************/ + +#include "bt_types.h" +#include "bt_target.h" +#include +#include "btm_int.h" + +/* Global BTM control block structure +*/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_CB btm_cb; +#endif + +/******************************************************************************* +** +** Function btm_init +** +** Description This function is called at BTM startup to allocate the +** control block (if using dynamic memory), and initializes the +** tracing level. It then initializes the various components of +** btm. +** +** Returns void +** +*******************************************************************************/ +void btm_init (void) +{ + /* All fields are cleared; nonzero fields are reinitialized in appropriate function */ + memset(&btm_cb, 0, sizeof(tBTM_CB)); + +#if defined(BTM_INITIAL_TRACE_LEVEL) + btm_cb.trace_level = BTM_INITIAL_TRACE_LEVEL; +#else + btm_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + /* Initialize BTM component structures */ + btm_inq_db_init(); /* Inquiry Database and Structures */ + btm_acl_init(); /* ACL Database and Structures */ + btm_sec_init(BTM_SEC_MODE_SP); /* Security Manager Database and Structures */ +#if BTM_SCO_INCLUDED == TRUE + btm_sco_init(); /* SCO Database and Structures (If included) */ +#endif + + btm_dev_init(); /* Device Manager Structures & HCI_Reset */ +} + + diff --git a/stack/btm/btm_pm.c b/stack/btm/btm_pm.c new file mode 100644 index 0000000..19b90d3 --- /dev/null +++ b/stack/btm/btm_pm.c @@ -0,0 +1,998 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains functions that manages ACL link modes. + * This includes operations such as active, hold, + * park and sniff modes. + * + * This module contains both internal and external (API) + * functions. External (API) functions are distinguishable + * by their names beginning with uppercase BTM. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include "bt_types.h" +#include "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "hcidefs.h" + + +#if BTM_PWR_MGR_INCLUDED == TRUE + +/* This compile option is only useful when the FW has a bug + * it automatically uses single slot when entering SNIFF mode, but does not restore the setting + * This issue was found when A2DP link goes into sniff and existing sniff still has choppy audio. + * If this issue is seen, look for FW solution first. + * This work around code will be removed later. */ +#ifndef BTM_PM_SNIFF_SLOT_WORK_AROUND +#define BTM_PM_SNIFF_SLOT_WORK_AROUND FALSE +#endif + +/*****************************************************************************/ +/* to handle different modes */ +/*****************************************************************************/ +#define BTM_PM_STORED_MASK 0x80 /* set this mask if the command is stored */ +#define BTM_PM_NUM_SET_MODES 3 /* only hold, sniff & park */ + +/* Usage: (ptr_features[ offset ] & mask )?TRUE:FALSE */ +/* offset to supported feature */ +const UINT8 btm_pm_mode_off[BTM_PM_NUM_SET_MODES] = {0, 0, 1}; +/* mask to supported feature */ +const UINT8 btm_pm_mode_msk[BTM_PM_NUM_SET_MODES] = {0x40, 0x80, 0x01}; + +#define BTM_PM_GET_MD1 1 +#define BTM_PM_GET_MD2 2 +#define BTM_PM_GET_COMP 3 + +const UINT8 btm_pm_md_comp_matrix[BTM_PM_NUM_SET_MODES*BTM_PM_NUM_SET_MODES] = +{ + BTM_PM_GET_COMP, + BTM_PM_GET_MD2, + BTM_PM_GET_MD2, + + BTM_PM_GET_MD1, + BTM_PM_GET_COMP, + BTM_PM_GET_MD1, + + BTM_PM_GET_MD1, + BTM_PM_GET_MD2, + BTM_PM_GET_COMP +}; + +/* function prototype */ +static int btm_pm_find_acl_ind(BD_ADDR remote_bda); +static tBTM_STATUS btm_pm_snd_md_req( UINT8 pm_id, int link_ind, tBTM_PM_PWR_MD *p_mode ); + +/* +#ifdef BTM_PM_DEBUG +#undef BTM_PM_DEBUG +#define BTM_PM_DEBUG TRUE +#endif +*/ + +#if BTM_PM_DEBUG == TRUE +const char * btm_pm_state_str[] = +{ + "pm_active_state", + "pm_hold_state", + "pm_sniff_state", + "pm_park_state", + "pm_pend_state" +}; + +const char * btm_pm_event_str[] = +{ + "pm_set_mode_event", + "pm_hci_sts_event", + "pm_mod_chg_event", + "pm_update_event" +}; + +const char * btm_pm_action_str[] = +{ + "pm_set_mode_action", + "pm_update_db_action", + "pm_mod_chg_action", + "pm_hci_sts_action", + "pm_update_action" +}; +#endif + +/*****************************************************************************/ +/* P U B L I C F U N C T I O N S */ +/*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_PmRegister +** +** Description register or deregister with power manager +** +** Returns BTM_SUCCESS if successful, +** BTM_NO_RESOURCES if no room to hold registration +** BTM_ILLEGAL_VALUE +** +*******************************************************************************/ +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, tBTM_PM_STATUS_CBACK *p_cb) +{ + int xx; + + /* de-register */ + if(mask & BTM_PM_DEREG) + { + if(*p_pm_id >= BTM_MAX_PM_RECORDS) + return BTM_ILLEGAL_VALUE; + btm_cb.pm_reg_db[*p_pm_id].mask = BTM_PM_REC_NOT_USED; + return BTM_SUCCESS; + } + + for(xx=0; xx= BTM_MAX_PM_RECORDS) + pm_id = BTM_PM_SET_ONLY_ID; + + if(p_mode == NULL) + return BTM_ILLEGAL_VALUE; + + BTM_TRACE_API3( "BTM_SetPowerMode: pm_id %d BDA: %08x mode:0x%x", pm_id, + (remote_bda[2]<<24)+(remote_bda[3]<<16)+(remote_bda[4]<<8)+remote_bda[5], p_mode->mode); + + /* take out the force bit */ + mode = p_mode->mode & ~BTM_PM_MD_FORCE; + + acl_ind = btm_pm_find_acl_ind(remote_bda); + if(acl_ind == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + p_cb = &(btm_cb.pm_mode_db[acl_ind]); + + if(mode != BTM_PM_MD_ACTIVE) + { + /* check if the requested mode is supported */ + ind = mode - BTM_PM_MD_HOLD; /* make it base 0 */ + p_features = BTM_ReadLocalFeatures(); + if( !(p_features[ btm_pm_mode_off[ind] ] & btm_pm_mode_msk[ind] ) ) + return BTM_MODE_UNSUPPORTED; + } + + if(mode == p_cb->state) /* the requested mode is current mode */ + { + /* already in the requested mode and the current interval has less latency than the max */ + if( (mode == BTM_PM_MD_ACTIVE) || + ((p_mode->mode & BTM_PM_MD_FORCE) && (p_mode->max >= p_cb->interval) && (p_mode->min <= p_cb->interval)) || + ((p_mode->mode & BTM_PM_MD_FORCE)==0 && (p_mode->max >= p_cb->interval)) ) + { + BTM_TRACE_DEBUG4( "BTM_SetPowerMode: mode:0x%x interval %d max:%d, min:%d", p_mode->mode, p_cb->interval, p_mode->max, p_mode->min); + return BTM_SUCCESS; + } + } + + temp_pm_id = pm_id; + if(pm_id == BTM_PM_SET_ONLY_ID) + temp_pm_id = BTM_MAX_PM_RECORDS; + + /* update mode database */ + if( ((pm_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[pm_id].mask & BTM_PM_REG_SET)) + || ((pm_id == BTM_PM_SET_ONLY_ID) && (btm_cb.pm_pend_link != MAX_L2CAP_LINKS)) ) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "BTM_SetPowerMode: Saving cmd acl_ind %d temp_pm_id %d", acl_ind,temp_pm_id); +#endif + /* Make sure mask is set to BTM_PM_REG_SET */ + btm_cb.pm_reg_db[temp_pm_id].mask |= BTM_PM_REG_SET; + *(&p_cb->req_mode[temp_pm_id]) = *((tBTM_PM_PWR_MD *)p_mode); + p_cb->chg_ind = TRUE; + } + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm state:0x%x, pm_pend_link: %d", p_cb->state, btm_cb.pm_pend_link); +#endif + /* if mode == hold or pending, return */ + if( (p_cb->state == BTM_PM_STS_HOLD) || + (p_cb->state == BTM_PM_STS_PENDING) || + (btm_cb.pm_pend_link != MAX_L2CAP_LINKS) ) /* command pending */ + { + if(acl_ind != btm_cb.pm_pend_link) + { + /* set the stored mask */ + p_cb->state |= BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG1( "btm_pm state stored:%d",acl_ind); + } + return BTM_CMD_STORED; + } + + + + return btm_pm_snd_md_req(pm_id, acl_ind, p_mode); +} + +/******************************************************************************* +** +** Function BTM_ReadPowerMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, tBTM_PM_MODE *p_mode) +{ + int acl_ind; + + if( (acl_ind = btm_pm_find_acl_ind(remote_bda)) == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + *p_mode = btm_cb.pm_mode_db[acl_ind].state; + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function BTM_SetSsrParams +** +** Description This sends the given SSR parameters for the given ACL +** connection if it is in ACTIVE mode. +** +** Input Param remote_bda - device address of desired ACL connection +** max_lat - maximum latency (in 0.625ms)(0-0xFFFE) +** min_rmt_to - minimum remote timeout +** min_loc_to - minimum local timeout +** +** +** Returns BTM_SUCCESS if the HCI command is issued successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** BTM_CMD_STORED if the command is stored +** +*******************************************************************************/ +tBTM_STATUS BTM_SetSsrParams (BD_ADDR remote_bda, UINT16 max_lat, + UINT16 min_rmt_to, UINT16 min_loc_to) +{ +#if (BTM_SSR_INCLUDED == TRUE) + int acl_ind; + tBTM_PM_MCB *p_cb; + + if( (acl_ind = btm_pm_find_acl_ind(remote_bda)) == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + if(BTM_PM_STS_ACTIVE == btm_cb.pm_mode_db[acl_ind].state || + BTM_PM_STS_SNIFF == btm_cb.pm_mode_db[acl_ind].state) + { + if (btsnd_hcic_sniff_sub_rate(btm_cb.acl_db[acl_ind].hci_handle, max_lat, + min_rmt_to, min_loc_to)) + return BTM_SUCCESS; + else + return BTM_NO_RESOURCES; + } + p_cb = &btm_cb.pm_mode_db[acl_ind]; + p_cb->max_lat = max_lat; + p_cb->min_rmt_to = min_rmt_to; + p_cb->min_loc_to = min_loc_to; + return BTM_CMD_STORED; +#else + return BTM_ILLEGAL_ACTION; +#endif +} + +/******************************************************************************* +** +** Function btm_pm_reset +** +** Description as a part of the BTM reset process. +** +** Returns void +** +*******************************************************************************/ +void btm_pm_reset(void) +{ + int xx; + tBTM_PM_STATUS_CBACK *cb = NULL; + + /* clear the pending request for application */ + if( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) + { + cb = btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback; + } + + /* no command pending */ + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + /* clear the register record */ + for(xx=0; xxstate = BTM_PM_ST_ACTIVE; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_sm_alloc ind:%d st:%d", ind, p_db->state); +#endif +} + +/******************************************************************************* +** +** Function btm_pm_find_acl_ind +** +** Description This function initializes the control block of an ACL link. +** It is called when an ACL connection is created. +** +** Returns void +** +*******************************************************************************/ +static int btm_pm_find_acl_ind(BD_ADDR remote_bda) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT8 xx; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if ((p->in_use) && (!memcmp (p->remote_addr, remote_bda, BD_ADDR_LEN))) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_find_acl_ind ind:%d, st:%d", xx, btm_cb.pm_mode_db[xx].state); +#endif + break; + } + } + return xx; +} + +/******************************************************************************* +** +** Function btm_pm_compare_modes +** Description get the "more active" mode of the 2 +** Returns void +** +*******************************************************************************/ +static tBTM_PM_PWR_MD * btm_pm_compare_modes(tBTM_PM_PWR_MD *p_md1, tBTM_PM_PWR_MD *p_md2, tBTM_PM_PWR_MD *p_res) +{ + UINT8 res; + + if(p_md1 == NULL) + { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + + return p_md2; + } + + if(p_md2->mode == BTM_PM_MD_ACTIVE || p_md1->mode == BTM_PM_MD_ACTIVE) + { + return NULL; + } + + /* check if force bit is involved */ + if(p_md1->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_md1; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + if(p_md2->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + res = (p_md1->mode - 1) * BTM_PM_NUM_SET_MODES + (p_md2->mode - 1); + res = btm_pm_md_comp_matrix[res]; + switch(res) + { + case BTM_PM_GET_MD1: + *p_res = *p_md1; + return p_md1; + + case BTM_PM_GET_MD2: + *p_res = *p_md2; + return p_md2; + + case BTM_PM_GET_COMP: + p_res->mode = p_md1->mode; + /* min of the two */ + p_res->max = (p_md1->max < p_md2->max)? (p_md1->max) : (p_md2->max); + /* max of the two */ + p_res->min = (p_md1->min > p_md2->min)? (p_md1->min) : (p_md2->min); + + /* the intersection is NULL */ + if( p_res->max < p_res->min) + return NULL; + + if(p_res->mode == BTM_PM_MD_SNIFF) + { + /* max of the two */ + p_res->attempt = (p_md1->attempt > p_md2->attempt)? (p_md1->attempt) : (p_md2->attempt); + p_res->timeout = (p_md1->timeout > p_md2->timeout)? (p_md1->timeout) : (p_md2->timeout); + } + return p_res; + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_pm_get_set_mode +** Description get the resulting mode from the registered parties, then compare it +** with the requested mode, if the command is from an unregistered party. +** Returns void +** +*******************************************************************************/ +static tBTM_PM_MODE btm_pm_get_set_mode(UINT8 pm_id, tBTM_PM_MCB *p_cb, tBTM_PM_PWR_MD *p_mode, tBTM_PM_PWR_MD *p_res) +{ + int xx, loop_max; + tBTM_PM_PWR_MD *p_md = NULL; + + if(p_mode != NULL && p_mode->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_mode; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res->mode; + } + + if(!p_mode) + loop_max = BTM_MAX_PM_RECORDS+1; + else + loop_max = BTM_MAX_PM_RECORDS; + + for( xx=0; xxreq_mode[xx].mode == BTM_PM_MD_ACTIVE) + { + /* if at least one registered (SET) party says ACTIVE, stay active */ + return BTM_PM_MD_ACTIVE; + } + else + { + /* if registered parties give conflicting information, stay active */ + if( (btm_pm_compare_modes(p_md, &p_cb->req_mode[xx], p_res)) == NULL) + return BTM_PM_MD_ACTIVE; + p_md = p_res; + } + } + } + + /* if the resulting mode is NULL(nobody registers SET), use the requested mode */ + if(p_md == NULL) + { + if(p_mode) + *p_res = *((tBTM_PM_PWR_MD *)p_mode); + else /* p_mode is NULL when btm_pm_snd_md_req is called from btm_pm_proc_mode_change */ + return BTM_PM_MD_ACTIVE; + } + else + { + /* if the command is from unregistered party, + compare the resulting mode from registered party*/ + if( (pm_id == BTM_PM_SET_ONLY_ID) && + ((btm_pm_compare_modes(p_mode, p_md, p_res)) == NULL) ) + return BTM_PM_MD_ACTIVE; + } + + return p_res->mode; +} + +/******************************************************************************* +** +** Function btm_pm_snd_md_req +** Description get the resulting mode and send the resuest to host controller +** Returns tBTM_STATUS +**, BOOLEAN *p_chg_ind +*******************************************************************************/ +static tBTM_STATUS btm_pm_snd_md_req(UINT8 pm_id, int link_ind, tBTM_PM_PWR_MD *p_mode) +{ + tBTM_PM_PWR_MD md_res; + tBTM_PM_MODE mode; + tBTM_PM_MCB *p_cb = &btm_cb.pm_mode_db[link_ind]; + BOOLEAN chg_ind = FALSE; + + mode = btm_pm_get_set_mode(pm_id, p_cb, p_mode, &md_res); + md_res.mode = mode; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_snd_md_req link_ind:%d, mode: %d", + link_ind, mode); +#endif + + if( p_cb->state == mode) + { + /* already in the resulting mode */ + if( (mode == BTM_PM_MD_ACTIVE) || + ((md_res.max >= p_cb->interval) && (md_res.min <= p_cb->interval)) ) + return BTM_CMD_STORED; + /* Otherwise, needs to wake, then sleep */ + chg_ind = TRUE; + } + p_cb->chg_ind = chg_ind; + + /* cannot go directly from current mode to resulting mode. */ + if( mode != BTM_PM_MD_ACTIVE && p_cb->state != BTM_PM_MD_ACTIVE) + p_cb->chg_ind = TRUE; /* needs to wake, then sleep */ + + if(p_cb->chg_ind == TRUE) /* needs to wake first */ + md_res.mode = BTM_PM_MD_ACTIVE; +#if (BTM_SSR_INCLUDED == TRUE) + else if(BTM_PM_MD_SNIFF == md_res.mode && p_cb->max_lat) + { + btsnd_hcic_sniff_sub_rate(btm_cb.acl_db[link_ind].hci_handle, p_cb->max_lat, + p_cb->min_rmt_to, p_cb->min_loc_to); + p_cb->max_lat = 0; + } +#endif + /* Default is failure */ + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + /* send the appropriate HCI command */ + btm_cb.pm_pend_id = pm_id; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2("btm_pm_snd_md_req state:0x%x, link_ind: %d", p_cb->state, link_ind); +#endif + switch(md_res.mode) + { + case BTM_PM_MD_ACTIVE: + switch(p_cb->state) + { + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_exit_sniff_mode(btm_cb.acl_db[link_ind].hci_handle)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + case BTM_PM_MD_PARK: + if (btsnd_hcic_exit_park_mode(btm_cb.acl_db[link_ind].hci_handle)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + break; + + case BTM_PM_MD_HOLD: + if (btsnd_hcic_hold_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_sniff_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min, md_res.attempt, + md_res.timeout)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + + case BTM_PM_MD_PARK: + if (btsnd_hcic_park_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + + if(btm_cb.pm_pend_link == MAX_L2CAP_LINKS) + { + /* the command was not sent */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "pm_pend_link: %d",btm_cb.pm_pend_link); +#endif + return (BTM_NO_RESOURCES); + } + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_pm_check_stored +** +** Description This function is called when an HCI command status event occurs +** to check if there's any PM command issued while waiting for +** HCI command status. +** +** Returns none. +** +*******************************************************************************/ +static void btm_pm_check_stored(void) +{ + int xx; + for(xx=0; xx= MAX_L2CAP_LINKS) + return; + + p_cb = &btm_cb.pm_mode_db[btm_cb.pm_pend_link]; + + if(status == HCI_SUCCESS) + { + p_cb->state = BTM_PM_ST_PENDING; + pm_status = BTM_PM_STS_PENDING; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "btm_pm_proc_cmd_status new state:0x%x", p_cb->state); +#endif + } + else /* the command was not successfull. Stay in the same state */ + { + pm_status = BTM_PM_STS_ERROR; + } + + /* notify the caller is appropriate */ + if( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) + { + (*btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback)(btm_cb.acl_db[btm_cb.pm_pend_link].remote_addr, pm_status, 0, status); + } + + /* no pending cmd now */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG3( "btm_pm_proc_cmd_status state:0x%x, pm_pend_link: %d(new: %d)", + p_cb->state, btm_cb.pm_pend_link, MAX_L2CAP_LINKS); +#endif + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + btm_pm_check_stored(); +} + +/******************************************************************************* +** +** Function btm_process_mode_change +** +** Description This function is called when an HCI mode change event occurs. +** +** Input Parms hci_status - status of the event (HCI_SUCCESS if no errors) +** hci_handle - connection handle associated with the change +** mode - HCI_MODE_ACTIVE, HCI_MODE_HOLD, HCI_MODE_SNIFF, or HCI_MODE_PARK +** interval - number of baseband slots (meaning depends on mode) +** +** Returns none. +** +*******************************************************************************/ +void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, UINT16 interval) +{ + tACL_CONN *p; + tBTM_PM_MCB *p_cb = NULL; + int xx, yy, zz; + tBTM_PM_STATE old_state; + tL2C_LCB *p_lcb; + + /* get the index to acl_db */ + if ((xx = btm_handle_to_acl_index(hci_handle)) >= MAX_L2CAP_LINKS) + return; + + p = &btm_cb.acl_db[xx]; + + /*** 2035 and 2045 work around: If mode is active and coming out of a SCO disconnect, restore packet types ***/ + if (mode == HCI_MODE_ACTIVE) + { + if(BTM_GetNumScoLinks() == 0) + { + if(p->restore_pkt_types) + { + BTM_TRACE_DEBUG3("btm mode change AFTER unsniffing; hci hdl 0x%x, types 0x%02x/0x%02x", + hci_handle, p->pkt_types_mask, p->restore_pkt_types); + p->pkt_types_mask = p->restore_pkt_types; + p->restore_pkt_types = 0; /* Only exists while SCO is active */ + btsnd_hcic_change_conn_type (p->hci_handle, p->pkt_types_mask); + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else + { + BTM_TRACE_DEBUG2("btm mode change AFTER unsniffing; hci hdl 0x%x, types 0x%02x", + hci_handle, btm_cb.btm_acl_pkt_types_supported); + btm_set_packet_types (p, btm_cb.btm_acl_pkt_types_supported); + } +#endif + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else + { + /* Mode changed from Sniff to Active while SCO is open. */ + /* Packet types of active mode, not sniff mode, should be used for ACL when SCO is closed. */ + p->restore_pkt_types = btm_cb.btm_acl_pkt_types_supported; + + /* Exclude packet types not supported by the peer */ + btm_acl_chk_peer_pkt_type_support (p, &p->restore_pkt_types); + } +#endif + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else if (mode == HCI_MODE_SNIFF) + { + BTM_TRACE_DEBUG1("btm mode change to sniff; hci hdl 0x%x use single slot", + hci_handle); + btm_set_packet_types (p, (HCI_PKT_TYPES_MASK_DM1 | HCI_PKT_TYPES_MASK_DH1)); + } +#endif + + /* update control block */ + p_cb = &(btm_cb.pm_mode_db[xx]); + old_state = p_cb->state; + p_cb->state = mode; + p_cb->interval = interval; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_proc_mode_change new state:0x%x (old:0x%x)", p_cb->state, old_state); +#endif + + if ((p_cb->state == HCI_MODE_ACTIVE) && + ((p_lcb = l2cu_find_lcb_by_bd_addr (p->remote_addr)) != NULL)) + { + /* There might be any pending packets due to SNIFF or PENDING state */ + /* Trigger L2C to start transmission of the pending packets. */ + BTM_TRACE_DEBUG0 ("btm mode change to active; check l2c_link for outgoing packets"); + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + //btu_stop_timer (&p_lcb->timer_entry); + } + + /* notify registered parties */ + for(yy=0; yy<=BTM_MAX_PM_RECORDS; yy++) + { + /* set req_mode HOLD mode->ACTIVE */ + if( (mode == BTM_PM_MD_ACTIVE) && (p_cb->req_mode[yy].mode == BTM_PM_MD_HOLD) ) + p_cb->req_mode[yy].mode = BTM_PM_MD_ACTIVE; + } + + /* new request has been made. - post a message to BTU task */ + if(old_state & BTM_PM_STORED_MASK) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "btm_pm_proc_mode_change: Sending stored req:%d", xx); +#endif + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, xx, NULL); + } + else + { + for(zz=0; zzremote_addr, mode, interval, hci_status); + } + } + + /* If mode change was because of an active role switch or change link key */ + btm_cont_rswitch_or_chglinkkey(p, btm_find_dev(p->remote_addr), hci_status); +} + +/******************************************************************************* +** +** Function btm_pm_proc_ssr_evt +** +** Description This function is called when an HCI sniff subrating event occurs. +** +** Returns none. +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT16 max_tx_lat, max_rx_lat; + int xx, yy; + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl=NULL; + UINT16 use_ssr = TRUE; + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + /* get the index to acl_db */ + if ((xx = btm_handle_to_acl_index(handle)) >= MAX_L2CAP_LINKS) + return; + + STREAM_TO_UINT16 (max_tx_lat, p); + STREAM_TO_UINT16 (max_rx_lat, p); + p_cb = &(btm_cb.pm_mode_db[xx]); + + p_acl = &btm_cb.acl_db[xx]; + if(p_cb->interval == max_rx_lat) + { + /* using legacy sniff */ + use_ssr = FALSE; + } + + /* notify registered parties */ + for(yy=0; yyremote_addr, BTM_PM_STS_SSR, use_ssr, status); + } + } + } +} +#endif +#else /* BTM_PWR_MGR_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Functions BTM_PmRegister, BTM_SetPowerMode, and BTM_ReadPowerMode +** +** Description Stubbed versions for BTM_PWR_MGR_INCLUDED = FALSE +** +** Returns BTM_MODE_UNSUPPORTED. +** +*******************************************************************************/ +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, tBTM_PM_STATUS_CBACK *p_cb) +{ + return BTM_MODE_UNSUPPORTED; +} + +tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, tBTM_PM_PWR_MD *p_mode) +{ + return BTM_MODE_UNSUPPORTED; +} + +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, tBTM_PM_MODE *p_mode) +{ + return BTM_MODE_UNSUPPORTED; +} + +#endif + +/******************************************************************************* +** +** Function BTM_IsPowerManagerOn +** +** Description This function is called to check if power manager is included. +** in the BTE version. +** +** Returns BTM_PWR_MGR_INCLUDED. +** +*******************************************************************************/ +BOOLEAN BTM_IsPowerManagerOn (void) +{ + return BTM_PWR_MGR_INCLUDED; +} diff --git a/stack/btm/btm_sco.c b/stack/btm/btm_sco.c new file mode 100644 index 0000000..5a11404 --- /dev/null +++ b/stack/btm/btm_sco.c @@ -0,0 +1,1750 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle SCO connections. This includes + * operations such as connect, disconnect, change supported packet types. + * + ******************************************************************************/ + +#include +#include "bt_types.h" +#include "bt_target.h" +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" + +#if BTM_SCO_INCLUDED == TRUE + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#define SCO_ST_UNUSED 0 +#define SCO_ST_LISTENING 1 +#define SCO_ST_W4_CONN_RSP 2 +#define SCO_ST_CONNECTING 3 +#define SCO_ST_CONNECTED 4 +#define SCO_ST_DISCONNECTING 5 +#define SCO_ST_PEND_UNPARK 6 +#define SCO_ST_PEND_ROLECHANGE 7 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ + +static const tBTM_ESCO_PARAMS btm_esco_defaults = +{ + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission Effort (Power) */ +}; + +/******************************************************************************* +** +** Function btm_sco_flush_sco_data +** +** Description This function is called to flush the SCO data for this channel. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_flush_sco_data(UINT16 sco_inx) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p ; + BT_HDR *p_buf; + + if (sco_inx < BTM_MAX_SCO_LINKS) + { + p = &btm_cb.sco_cb.sco_db[sco_inx]; + while (p->xmit_data_q.p_first) + { + if ((p_buf = (BT_HDR *)GKI_dequeue (&p->xmit_data_q)) != NULL) + GKI_freebuf (p_buf); + } + } +#endif +#endif +} +/******************************************************************************* +** +** Function btm_sco_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_sco_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.sco_cb, 0, sizeof(tSCO_CB)); +#endif + /* Initialize nonzero defaults */ + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + + btm_cb.sco_cb.def_esco_parms = btm_esco_defaults; /* Initialize with defaults */ + btm_cb.sco_cb.desired_sco_mode = BTM_DEFAULT_SCO_MODE; +} + +/******************************************************************************* +** +** Function btm_esco_conn_rsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then default values are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** Returns void +** +*******************************************************************************/ +static void btm_esco_conn_rsp (UINT16 sco_inx, UINT8 hci_status, BD_ADDR bda, + tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco = NULL; + tBTM_ESCO_PARAMS *p_setup; + UINT16 temp_pkt_types; + + if (sco_inx < BTM_MAX_SCO_LINKS) + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Reject the connect request if refused by caller or wrong state */ + if (hci_status != HCI_SUCCESS || p_sco == NULL) + { + if (p_sco) + { + p_sco->state = (p_sco->state == SCO_ST_W4_CONN_RSP) ? SCO_ST_LISTENING + : SCO_ST_UNUSED; + } + + if (!btm_cb.sco_cb.esco_supported) + { + if (!btsnd_hcic_reject_conn (bda, hci_status)) + { + BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + else + { + if (!btsnd_hcic_reject_esco_conn (bda, hci_status)) + { + BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + } + else /* Connection is being accepted */ + { + p_sco->state = SCO_ST_CONNECTING; + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_1_2) + { + p_setup = &p_sco->esco.setup; + /* If parameters not specified use the default */ + if (p_parms) + *p_setup = *p_parms; + else /* Use the last setup passed thru BTM_SetEscoMode (or defaults) */ + { + *p_setup = btm_cb.sco_cb.def_esco_parms; + } + + temp_pkt_types = (p_setup->packet_types & + BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* Make sure at least one eSCO packet type is sent, else might confuse peer */ + /* Taking this out to confirm with BQB tests + ** Real application would like to include this though, as many devices + ** do not retry with SCO only if an eSCO connection fails. + if (!(temp_pkt_types & BTM_ESCO_LINK_ONLY_MASK)) + { + temp_pkt_types |= BTM_SCO_PKT_TYPES_MASK_EV3; + } + */ + /* If SCO request, remove eSCO packet types (conformance) */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO) + { + temp_pkt_types &= BTM_SCO_LINK_ONLY_MASK; + + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + /* OR in any exception packet types if at least 2.0 version of spec */ + else if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + if (btsnd_hcic_accept_esco_conn (bda, p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types)) + { + p_setup->packet_types = temp_pkt_types; + } + else + { + BTM_TRACE_ERROR0("Could not accept SCO conn: No Buffer!!!"); + } + } + else /* Controller is version 1.1 or earlier */ + { + btsnd_hcic_accept_conn (bda, 0); + } + } +#endif +} + + +#if BTM_SCO_HCI_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_sco_check_send_pkts +** +** Description This function is called to check if it can send packets +** to the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_check_send_pkts (UINT16 sco_inx) +{ + BT_HDR *p_buf; + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN *p_ccb = &p_cb->sco_db[sco_inx]; + + /* If there is data to send, send it now */ + while (p_ccb->xmit_data_q.p_first != NULL) + { + p_buf = NULL; + +#if BTM_SCO_HCI_DEBUG + BTM_TRACE_DEBUG1 ("btm: [%d] buf in xmit_data_q", p_ccb->xmit_data_q.count ); +#endif + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_data_q); + + HCI_SCO_DATA_TO_LOWER (p_buf); + } +} +#endif /* BTM_SCO_HCI_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function btm_route_sco_data +** +** Description Route received SCO data. +** +** Returns void +** +*******************************************************************************/ +void btm_route_sco_data(BT_HDR *p_msg) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE + UINT16 sco_inx, handle; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 pkt_size = 0; + UINT8 pkt_status = 0; + + /* Extract Packet_Status_Flag and handle */ + STREAM_TO_UINT16 (handle, p); + pkt_status = HCID_GET_EVENT(handle); + handle = HCID_GET_HANDLE (handle); + + STREAM_TO_UINT8 (pkt_size, p); + + if ((sco_inx = btm_find_scb_by_handle(handle)) != BTM_MAX_SCO_LINKS ) + { + /* send data callback */ + if (!btm_cb.sco_cb.p_data_cb ) + /* if no data callback registered, just free the buffer */ + GKI_freebuf (p_msg); + else + { + (*btm_cb.sco_cb.p_data_cb)(sco_inx, p_msg, (tBTM_SCO_DATA_FLAG) pkt_status); + } + } + else /* no mapping handle SCO connection is active, free the buffer */ + { + GKI_freebuf (p_msg); + } +#else + GKI_freebuf(p_msg); +#endif +} + + + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf) +{ +#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT8 *p; + tBTM_STATUS status = BTM_SUCCESS; + + if (sco_inx < BTM_MAX_SCO_LINKS && btm_cb.sco_cb.p_data_cb && + p_ccb->state == SCO_ST_CONNECTED) + { + /* Ensure we have enough space in the buffer for the SCO and HCI headers */ + if (p_buf->offset < HCI_SCO_PREAMBLE_SIZE) + { + BTM_TRACE_ERROR1 ("BTM SCO - cannot send buffer, offset: %d", p_buf->offset); + GKI_freebuf (p_buf); + status = BTM_ILLEGAL_VALUE; + } + else /* write HCI header */ + { + /* Step back 3 bytes to add the headers */ + p_buf->offset -= HCI_SCO_PREAMBLE_SIZE; + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + /* add HCI handle */ + UINT16_TO_STREAM (p, p_ccb->hci_handle); + /* only sent the first BTM_SCO_DATA_SIZE_MAX bytes data if more than max, + and set warning status */ + if (p_buf->len > BTM_SCO_DATA_SIZE_MAX) + { + p_buf->len = BTM_SCO_DATA_SIZE_MAX; + status = BTM_SCO_BAD_LENGTH; + } + + UINT8_TO_STREAM (p, (UINT8)p_buf->len); + p_buf->len += HCI_SCO_PREAMBLE_SIZE; + + GKI_enqueue (&p_ccb->xmit_data_q, p_buf); + + btm_sco_check_send_pkts (sco_inx); + } + } + else + { + GKI_freebuf(p_buf); + + BTM_TRACE_WARNING2 ("BTM_WriteScoData, invalid sco index: %d at state [%d]", + sco_inx, btm_cb.sco_cb.sco_db[sco_inx].state); + status = BTM_UNKNOWN_ADDR; + } + + return (status); + +#else + return (BTM_NO_RESOURCES); +#endif +} + +#if (BTM_MAX_SCO_LINKS>0) +/******************************************************************************* +** +** Function btm_send_connect_request +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_send_connect_request(UINT16 acl_handle, + tBTM_ESCO_PARAMS *p_setup) +{ + UINT16 temp_pkt_types; + UINT8 xx; + tACL_CONN *p_acl; + + /* Send connect request depending on version of spec */ + if (!btm_cb.sco_cb.esco_supported) + { + if (!btsnd_hcic_add_SCO_conn (acl_handle, BTM_ESCO_2_SCO(p_setup->packet_types))) + return (BTM_NO_RESOURCES); + } + else + { + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + /* Finally, remove EDR eSCO if the remote device doesn't support it */ + /* UPF25: Only SCO was brought up in this case */ + btm_handle_to_acl_index(acl_handle); + if ((xx = btm_handle_to_acl_index(acl_handle)) < MAX_L2CAP_LINKS) + { + p_acl = &btm_cb.acl_db[xx]; + if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->features)) + { + + BTM_TRACE_WARNING0("BTM Remote does not support 2-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5); + } + if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->features)) + { + + BTM_TRACE_WARNING0("BTM Remote does not support 3-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5); + } + } + + + BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types); + + if (!btsnd_hcic_setup_esco_conn(acl_handle, + p_setup->tx_bw, + p_setup->rx_bw, + p_setup->max_latency, + p_setup->voice_contfmt, + p_setup->retrans_effort, + temp_pkt_types)) + return (BTM_NO_RESOURCES); + else + p_setup->packet_types = temp_pkt_types; + } + + return (BTM_CMD_STARTED); +} +#endif + +/******************************************************************************* +** +** Function btm_set_sco_ind_cback +** +** Description This function is called to register for TCS SCO connect +** indications. +** +** Returns void +** +*******************************************************************************/ +void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ) +{ + btm_cb.sco_cb.app_sco_ind_cb = sco_ind_cb; +} + +/******************************************************************************* +** +** Function btm_accept_sco_link +** +** Description This function is called to respond to TCS SCO connect +** indications +** +** Returns void +** +*******************************************************************************/ +void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco; + + if (sco_inx >= BTM_MAX_SCO_LINKS) + { + BTM_TRACE_ERROR1("btm_accept_sco_link: Invalid sco_inx(%d)", sco_inx); + return; + } + + /* Link role is ignored in for this message */ + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_sco->p_conn_cb = p_conn_cb; + p_sco->p_disc_cb = p_disc_cb; + p_sco->esco.data.link_type = BTM_LINK_TYPE_ESCO; /* Accept with all supported types */ + + BTM_TRACE_DEBUG1("TCS accept SCO: Packet Types 0x%04x", p_setup->packet_types); + + btm_esco_conn_rsp(sco_inx, HCI_SUCCESS, p_sco->esco.data.bd_addr, p_setup); +#else + btm_reject_sco_link(sco_inx); +#endif +} + +/******************************************************************************* +** +** Function btm_reject_sco_link +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +void btm_reject_sco_link( UINT16 sco_inx ) +{ + btm_esco_conn_rsp(sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, NULL); +} + +/******************************************************************************* +** +** Function BTM_CreateSco +** +** Description This function is called to create an SCO connection. If the +** "is_orig" flag is TRUE, the connection will be originated, +** otherwise BTM will wait for the other side to connect. +** +** NOTE: If BTM_IGNORE_SCO_PKT_TYPE is passed in the pkt_types +** parameter the default packet types is used. +** +** Returns BTM_UNKNOWN_ADDR if the ACL connection is not up +** BTM_BUSY if another SCO being set up to +** the same BD address +** BTM_NO_RESOURCES if the max SCO limit has been reached +** BTM_CMD_STARTED if the connection establishment is started. +** In this case, "*p_sco_inx" is filled in +** with the sco index used for the connection. +** +*******************************************************************************/ +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, UINT16 pkt_types, + UINT16 *p_sco_inx, tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS > 0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT16 acl_handle = 0; + UINT16 temp_pkt_types; + tACL_CONN *p_acl; + +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) + tBTM_PM_MODE md; + tBTM_PM_PWR_MD pm; +#else + UINT8 mode; +#endif + + *p_sco_inx = BTM_INVALID_SCO_INDEX; + + /* If originating, ensure that there is an ACL connection to the BD Address */ + if (is_orig) + { + if ((!remote_bda) || ((acl_handle = BTM_GetHCIConnHandle (remote_bda)) == 0xFFFF)) + return (BTM_UNKNOWN_ADDR); + } + + if (remote_bda) + { + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (((p->state == SCO_ST_CONNECTING) || (p->state == SCO_ST_LISTENING) + || (p->state == SCO_ST_PEND_UNPARK)) + && (!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN))) + { + return (BTM_BUSY); + } + } + } + else + { + /* Support only 1 wildcard BD address at a time */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_LISTENING) && (!p->rem_bd_known)) + return (BTM_BUSY); + } + } + + /* Now, try to find an unused control block, and kick off the SCO establishment */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_UNUSED) + { + if (remote_bda) + { + if (is_orig) + { + /* can not create SCO link if in park mode */ +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) + if(BTM_ReadPowerMode(remote_bda, &md) == BTM_SUCCESS) + { + if (md == BTM_PM_MD_PARK || md == BTM_PM_MD_SNIFF) + { +/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode + the other data members of tBTM_PM_PWR_MD are ignored +*/ + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, remote_bda, &pm); + p->state = SCO_ST_PEND_UNPARK; + } + } +#elif BTM_PWR_MGR_INCLUDED == TRUE + if( (BTM_ReadPowerMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_PM_MD_PARK) ) + return (BTM_WRONG_MODE); +#else + if( (BTM_ReadAclMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_ACL_MODE_PARK) ) + return (BTM_WRONG_MODE); +#endif + } + memcpy (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + } + else + p->rem_bd_known = FALSE; + + /* Link role is ignored in for this message */ + if (pkt_types == BTM_IGNORE_SCO_PKT_TYPE) + pkt_types = btm_cb.sco_cb.def_esco_parms.packet_types; + + p_setup = &p->esco.setup; + *p_setup = btm_cb.sco_cb.def_esco_parms; + p_setup->packet_types = (btm_cb.sco_cb.desired_sco_mode == BTM_LINK_TYPE_SCO) + ? (pkt_types & BTM_SCO_LINK_ONLY_MASK) : pkt_types; + + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + if (btm_cb.sco_cb.desired_sco_mode == HCI_LINK_TYPE_ESCO) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + else /* Only using SCO packet types; turn off EDR also */ + { + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + + p_setup->packet_types = temp_pkt_types; + p->p_conn_cb = p_conn_cb; + p->p_disc_cb = p_disc_cb; + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->is_orig = is_orig; + + if( p->state != SCO_ST_PEND_UNPARK ) + { + if (is_orig) + { + /* If role change is in progress, do not proceed with SCO setup + * Wait till role change is complete */ + p_acl = btm_bda_to_acl(remote_bda); + if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) + { + BTM_TRACE_API1("Role Change is in progress for ACL handle 0x%04x",acl_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + + } + } + } + + if( p->state != SCO_ST_PEND_UNPARK && p->state != SCO_ST_PEND_ROLECHANGE ) + { + if (is_orig) + { + BTM_TRACE_API2("BTM_CreateSco -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d", + acl_handle, btm_cb.sco_cb.desired_sco_mode); + + if ((btm_send_connect_request(acl_handle, p_setup)) != BTM_CMD_STARTED) + return (BTM_NO_RESOURCES); + + p->state = SCO_ST_CONNECTING; + } + else + p->state = SCO_ST_LISTENING; + } + + *p_sco_inx = xx; + + return (BTM_CMD_STARTED); + } + } + +#endif + /* If here, all SCO blocks in use */ + return (BTM_NO_RESOURCES); +} + +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) +/******************************************************************************* +** +** Function btm_sco_chk_pend_unpark +** +** Description This function is called by BTIF when there is a mode change +** event to see if there are SCO commands waiting for the unpark. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_PEND_UNPARK) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr)) == hci_handle)) + + { + BTM_TRACE_API3("btm_sco_chk_pend_unpark -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d, hci_status 0x%02x", + acl_handle, btm_cb.sco_cb.desired_sco_mode, hci_status); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) + p->state = SCO_ST_CONNECTING; + } + } +#endif +} +#endif + +/******************************************************************************* +** +** Function btm_sco_chk_pend_rolechange +** +** Description This function is called by BTIF when there is a role change +** event to see if there are SCO commands waiting for the role change. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_rolechange (UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_PEND_ROLECHANGE) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr)) == hci_handle)) + + { + BTM_TRACE_API1("btm_sco_chk_pend_rolechange -> (e)SCO Link for ACL handle 0x%04x", acl_handle); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) + p->state = SCO_ST_CONNECTING; + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_conn_req +** +** Description This function is called by BTIF when an SCO connection +** request is received from a remote. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CB *p_sco = &btm_cb.sco_cb; + tSCO_CONN *p = &p_sco->sco_db[0]; + UINT16 xx; + tBTM_ESCO_CONN_REQ_EVT_DATA evt_data; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + /* + * If the sco state is in the SCO_ST_CONNECTING state, we still need + * to return accept sco to avoid race conditon for sco creation + */ + if (((p->state == SCO_ST_LISTENING && p->rem_bd_known) || p->state == SCO_ST_CONNECTING) + && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + /* If this guy was a wildcard, he is not one any more */ + p->rem_bd_known = TRUE; + p->esco.data.link_type = link_type; + p->state = SCO_ST_W4_CONN_RSP; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + + /* If no callback, auto-accept the connection if packet types match */ + if (!p->esco.p_esco_cback) + { + /* If requesting eSCO reject if default parameters are SCO only */ + if ((link_type == BTM_LINK_TYPE_ESCO + && !(p_sco->def_esco_parms.packet_types & BTM_ESCO_LINK_ONLY_MASK) + && ((p_sco->def_esco_parms.packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) + == BTM_SCO_EXCEPTION_PKTS_MASK)) + + /* Reject request if SCO is desired but no SCO packets delected */ + || (link_type == BTM_LINK_TYPE_SCO + && !(p_sco->def_esco_parms.packet_types & BTM_SCO_LINK_ONLY_MASK))) + { + btm_esco_conn_rsp(xx, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); + } + else /* Accept the request */ + { + btm_esco_conn_rsp(xx, HCI_SUCCESS, bda, NULL); + } + } + else /* Notify upper layer of connect indication */ + { + memcpy(evt_data.bd_addr, bda, BD_ADDR_LEN); + memcpy(evt_data.dev_class, dev_class, DEV_CLASS_LEN); + evt_data.link_type = link_type; + evt_data.sco_inx = xx; + p->esco.p_esco_cback(BTM_ESCO_CONN_REQ_EVT, (tBTM_ESCO_EVT_DATA *)&evt_data); + } + + return; + } + } + + /* TCS usage */ + if (btm_cb.sco_cb.app_sco_ind_cb) + { + /* Now, try to find an unused control block */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_UNUSED) + { + p->is_orig = FALSE; + p->state = SCO_ST_LISTENING; + + p->esco.data.link_type = link_type; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + break; + } + } + if( xx < BTM_MAX_SCO_LINKS) + { + btm_cb.sco_cb.app_sco_ind_cb(xx); + return; + } + } + +#endif + /* If here, no one wants the SCO connection. Reject it */ + BTM_TRACE_WARNING0("btm_sco_conn_req: No one wants this SCO connection; rejecting it"); + btm_esco_conn_rsp(BTM_MAX_SCO_LINKS, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); +} + +/******************************************************************************* +** +** Function btm_sco_connected +** +** Description This function is called by BTIF when an (e)SCO connection +** is connected. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + BOOLEAN spt = FALSE; + tBTM_CHG_ESCO_PARAMS parms; +#endif + + btm_cb.sco_cb.sco_disc_reason = hci_status; + +#if (BTM_MAX_SCO_LINKS>0) + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (((p->state == SCO_ST_CONNECTING) || + (p->state == SCO_ST_LISTENING) || + (p->state == SCO_ST_W4_CONN_RSP)) + && (p->rem_bd_known) + && (!bda || !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + if (hci_status != HCI_SUCCESS) + { + /* Report the error if originator, otherwise remain in Listen mode */ + if (p->is_orig) + { + /* If role switch is pending, we need try again after role switch is complete */ + if(hci_status == HCI_ERR_ROLE_SWITCH_PENDING) + { + BTM_TRACE_API1("Role Change pending for HCI handle 0x%04x",hci_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + } + /* avoid calling disconnect callback because of sco creation race */ + else if (hci_status != HCI_ERR_LMP_ERR_TRANS_COLLISION) + { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + } + else + { + /* Notify the upper layer that incoming sco connection has failed. */ + if (p->state == SCO_ST_CONNECTING) + { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + else + p->state = SCO_ST_LISTENING; + } + + return; + } + + if (p->state == SCO_ST_LISTENING) + spt = TRUE; + + p->state = SCO_ST_CONNECTED; + p->hci_handle = hci_handle; + + if (!btm_cb.sco_cb.esco_supported) + { + p->esco.data.link_type = BTM_LINK_TYPE_SCO; + if (spt) + { + parms.packet_types = p->esco.setup.packet_types; + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + BTM_ChangeEScoLinkParms(xx, &parms); + } + } + else + { + if (p_esco_data) + p->esco.data = *p_esco_data; + } + + (*p->p_conn_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_find_scb_by_handle +** +** Description Look through all active SCO connection for a match based on the +** HCI handle. +** +** Returns index to matched SCO connection CB, or BTM_MAX_SCO_LINKS if +** no match. +** +*******************************************************************************/ +UINT16 btm_find_scb_by_handle (UINT16 handle) +{ + int xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_CONNECTED) && (p->hci_handle == handle)) + { + return (xx); + } + } + + /* If here, no match found */ + return (xx); +} + +/******************************************************************************* +** +** Function BTM_RemoveSco +** +** Description This function is called to remove a specific SCO connection. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT16 tempstate; + + /* Validity check */ + if ((sco_inx >= BTM_MAX_SCO_LINKS) || (p->state == SCO_ST_UNUSED)) + return (BTM_UNKNOWN_ADDR); + + /* If no HCI handle, simply drop the connection and return */ + if (p->hci_handle == BTM_INVALID_HCI_HANDLE || p->state == SCO_ST_PEND_UNPARK) + { + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister the eSCO event callback */ + return (BTM_SUCCESS); + } + + tempstate = p->state; + p->state = SCO_ST_DISCONNECTING; + + if (!btsnd_hcic_disconnect (p->hci_handle, HCI_ERR_PEER_USER)) + { + p->state = tempstate; + return (BTM_NO_RESOURCES); + } + + return (BTM_CMD_STARTED); +#else + return (BTM_NO_RESOURCES); +#endif +} + +/******************************************************************************* +** +** Function btm_remove_sco_links +** +** Description This function is called to remove all sco links for an ACL link. +** +** Returns void +** +*******************************************************************************/ +void btm_remove_sco_links (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->rem_bd_known && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + BTM_RemoveSco(xx); + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_removed +** +** Description This function is called by BTIF when an SCO connection +** is removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_removed (UINT16 hci_handle, UINT8 reason) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; +#endif + + btm_cb.sco_cb.sco_disc_reason = reason; + +#if (BTM_MAX_SCO_LINKS>0) + p = &btm_cb.sco_cb.sco_db[0]; + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state != SCO_ST_UNUSED) && (p->state != SCO_ST_LISTENING) && (p->hci_handle == hci_handle)) + { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->rem_bd_known = FALSE; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_sco_acl_removed +** +** Description This function is called when an ACL connection is +** removed. If the BD address is NULL, it is assumed that +** the local device is down, and all SCO links are removed. +** If a specific BD address is passed, only SCO connections +** to that BD address are removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_acl_removed (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state != SCO_ST_UNUSED) + { + if ((!bda) || (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN) && p->rem_bd_known)) + { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + } + } + } +#endif +} + + +/******************************************************************************* +** +** Function BTM_SetScoPacketTypes +** +** Description This function is called to set the packet types used for +** a specific SCO connection, +** +** Parameters pkt_types - One or more of the following +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +** BTM_SCO_LINK_ALL_MASK - enables all supported types +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_CHG_ESCO_PARAMS parms; + tSCO_CONN *p; + + /* Validity check */ + if (sco_inx >= BTM_MAX_SCO_LINKS) + return (BTM_UNKNOWN_ADDR); + + p = &btm_cb.sco_cb.sco_db[sco_inx]; + parms.packet_types = pkt_types; + + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + return (BTM_ChangeEScoLinkParms(sco_inx, &parms)); +#else + return (BTM_UNKNOWN_ADDR); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadScoPacketTypes +** +** Description This function is read the packet types used for a specific +** SCO connection. +** +** Returns Packet types supported for the connection +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) + return (p->esco.setup.packet_types); + else + return (0); +#else + return (0); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoDiscReason +** +** Description This function is returns the reason why an (e)SCO connection +** has been removed. It contains the value until read, or until +** another (e)SCO connection has disconnected. +** +** Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. +** +*******************************************************************************/ +UINT16 BTM_ReadScoDiscReason (void) +{ + UINT16 res = btm_cb.sco_cb.sco_disc_reason; + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + return (res); +} + +/******************************************************************************* +** +** Function BTM_ReadDeviceScoPacketTypes +** +** Description This function is read the SCO packet types that +** the device supports. +** +** Returns Packet types supported by the device. +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadDeviceScoPacketTypes (void) +{ + return (btm_cb.btm_sco_pkt_types_supported); +} + +/******************************************************************************* +** +** Function BTM_ReadScoHandle +** +** Description This function is used to read the HCI handle used for a specific +** SCO connection, +** +** Returns handle for the connection, or 0xFFFF if invalid SCO index. +** +*******************************************************************************/ +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) + return (p->hci_handle); + else + return (BTM_INVALID_HCI_HANDLE); +#else + return (BTM_INVALID_HCI_HANDLE); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoBdAddr +** +** Description This function is read the remote BD Address for a specific +** SCO connection, +** +** Returns pointer to BD address or NULL if not known +** +*******************************************************************************/ +UINT8 *BTM_ReadScoBdAddr (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->rem_bd_known)) + return (p->esco.data.bd_addr); + else + return (NULL); +#else + return (NULL); +#endif +} + +/******************************************************************************* +** +** Function BTM_SetEScoMode +** +** Description This function sets up the negotiated parameters for SCO or +** eSCO, and sets as the default mode used for outgoing calls to +** BTM_CreateSco. It does not change any currently active (e)SCO links. +** Note: Incoming (e)SCO connections will always use packet types +** supported by the controller. If eSCO is not desired the +** feature should be disabled in the controller's feature mask. +** +** Returns BTM_SUCCESS if the successful. +** BTM_BUSY if there are one or more active (e)SCO links. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) +{ + tSCO_CB *p_esco = &btm_cb.sco_cb; + tBTM_ESCO_PARAMS *p_def = &p_esco->def_esco_parms; + + if (p_esco->esco_supported) + { + if (p_parms) + { + if (sco_mode == BTM_LINK_TYPE_ESCO) + *p_def = *p_parms; /* Save as the default parameters */ + else /* Load only the SCO packet types */ + { + p_def->packet_types = p_parms->packet_types; + p_def->tx_bw = BTM_64KBITS_RATE; + p_def->rx_bw = BTM_64KBITS_RATE; + p_def->max_latency = 0x000a; + p_def->voice_contfmt = 0x0060; + p_def->retrans_effort = 0; + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + p_def->packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + } + p_esco->desired_sco_mode = sco_mode; + BTM_TRACE_API1("BTM_SetEScoMode -> mode %d", sco_mode); + } + else + { + p_esco->desired_sco_mode = BTM_LINK_TYPE_SCO; + p_def->packet_types &= BTM_SCO_LINK_ONLY_MASK; + p_def->retrans_effort = 0; + BTM_TRACE_API0("BTM_SetEScoMode -> mode SCO (eSCO not supported)"); + } + + BTM_TRACE_DEBUG6(" txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, voice 0x%04x, pkt 0x%04x, rtx effort 0x%02x", + p_def->tx_bw, p_def->rx_bw, p_def->max_latency, + p_def->voice_contfmt, p_def->packet_types, + p_def->retrans_effort); + + return (BTM_SUCCESS); +} + + + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** BTM_MODE_UNSUPPORTED if controller version is not BT1.2 or +** later or does not support eSCO. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (!btm_cb.sco_cb.esco_supported) + { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = NULL; + return (BTM_MODE_UNSUPPORTED); + } + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_UNUSED) + { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = p_esco_cback; + return (BTM_SUCCESS); + } + return (BTM_ILLEGAL_VALUE); +#else + return (BTM_MODE_UNSUPPORTED); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** Note: If called over a 1.1 controller, only the packet types +** field has meaning. +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + BTM_TRACE_API1("BTM_ReadEScoLinkParms -> sco_inx 0x%04x", sco_inx); + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) + { + *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data; + return (BTM_SUCCESS); + } +#endif + + memset(p_parms, 0, sizeof(tBTM_ESCO_DATA)); + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** Note: If called over a SCO link (including 1.1 controller), +** a change packet type request is sent out instead. +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p_sco; + UINT16 temp_pkt_types; + + /* Make sure sco handle is valid and on an active link */ + if (sco_inx >= BTM_MAX_SCO_LINKS || + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_CONNECTED) + return (BTM_WRONG_MODE); + + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_setup = &p_sco->esco.setup; + + /* If SCO connection OR eSCO not supported just send change packet types */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO || + !btm_cb.sco_cb.esco_supported) + { + p_setup->packet_types = p_parms->packet_types & + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_LINK_ONLY_MASK); + + + BTM_TRACE_API2("BTM_ChangeEScoLinkParms -> SCO Link for handle 0x%04x, pkt 0x%04x", + p_sco->hci_handle, p_setup->packet_types); + + if (!btsnd_hcic_change_conn_type (p_sco->hci_handle, + BTM_ESCO_2_SCO(p_setup->packet_types))) + return (BTM_NO_RESOURCES); + } + else + { + temp_pkt_types = (p_parms->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_parms->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + BTM_TRACE_API1("BTM_ChangeEScoLinkParms -> eSCO Link for handle 0x%04x", p_sco->hci_handle); + BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, p_parms->retrans_effort, temp_pkt_types); + + /* When changing an existing link, only change latency, retrans, and pkts */ + if (!btsnd_hcic_setup_esco_conn(p_sco->hci_handle, p_setup->tx_bw, + p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, + p_parms->retrans_effort, + temp_pkt_types)) + return (BTM_NO_RESOURCES); + else + p_parms->packet_types = temp_pkt_types; + } + + return (BTM_CMD_STARTED); +#else + return (BTM_WRONG_MODE); +#endif +} + +/******************************************************************************* +** +** Function BTM_EScoConnRsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then values set through BTM_SetEScoMode +** are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** +** Returns void +** +*******************************************************************************/ +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state == SCO_ST_W4_CONN_RSP) + { + btm_esco_conn_rsp(sco_inx, hci_status, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, + p_parms); + } +#endif +} + +/******************************************************************************* +** +** Function btm_read_def_esco_mode +** +** Description This function copies the current default esco settings into +** the return buffer. +** +** Returns tBTM_SCO_TYPE +** +*******************************************************************************/ +tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + *p_parms = btm_cb.sco_cb.def_esco_parms; + return btm_cb.sco_cb.desired_sco_mode; +#else + return BTM_LINK_TYPE_SCO; +#endif +} + +/******************************************************************************* +** +** Function btm_esco_proc_conn_chg +** +** Description This function is called by BTIF when an SCO connection +** is changed. +** +** Returns void +** +*******************************************************************************/ +void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + tBTM_CHG_ESCO_EVT_DATA data; + UINT16 xx; + + BTM_TRACE_EVENT2("btm_esco_proc_conn_chg -> handle 0x%04x, status 0x%02x", + handle, status); + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_CONNECTED && handle == p->hci_handle) + { + /* If upper layer wants notification */ + if (p->esco.p_esco_cback) + { + memcpy(data.bd_addr, p->esco.data.bd_addr, BD_ADDR_LEN); + data.hci_status = status; + data.sco_inx = xx; + data.rx_pkt_len = p->esco.data.rx_pkt_len = rx_pkt_len; + data.tx_pkt_len = p->esco.data.tx_pkt_len = tx_pkt_len; + data.tx_interval = p->esco.data.tx_interval = tx_interval; + data.retrans_window = p->esco.data.retrans_window = retrans_window; + + (*p->esco.p_esco_cback)(BTM_ESCO_CHG_EVT, + (tBTM_ESCO_EVT_DATA *)&data); + } + return; + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_is_sco_active +** +** Description This function is called to see if a SCO handle is already in +** use. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active (UINT16 handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (handle == p->hci_handle && p->state == SCO_ST_CONNECTED) + return (TRUE); + } +#endif + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetNumScoLinks +** +** Description This function returns the number of active sco links. +** +** Returns UINT8 +** +*******************************************************************************/ +UINT8 BTM_GetNumScoLinks (void) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT8 num_scos = 0; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + switch (p->state) + { + case SCO_ST_W4_CONN_RSP: + case SCO_ST_CONNECTING: + case SCO_ST_CONNECTED: + case SCO_ST_DISCONNECTING: + case SCO_ST_PEND_UNPARK: + num_scos++; + } + } + return (num_scos); +#else + return (0); +#endif +} + + +/******************************************************************************* +** +** Function btm_is_sco_active_by_bdaddr +** +** Description This function is called to see if a SCO active to a bd address. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT8 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN)) && (p->state == SCO_ST_CONNECTED)) + { + return (TRUE); + } + } +#endif + return (FALSE); +} +#else /* SCO_EXCLUDED == TRUE (Link in stubs) */ + +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, + UINT16 pkt_types, UINT16 *p_sco_inx, + tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) {return (BTM_NO_RESOURCES);} +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) {return (BTM_NO_RESOURCES);} +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) {return (BTM_NO_RESOURCES);} +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) {return (0);} +UINT16 BTM_ReadDeviceScoPacketTypes (void) {return (0);} +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) {return (BTM_INVALID_HCI_HANDLE);} +UINT8 *BTM_ReadScoBdAddr(UINT16 sco_inx) {return((UINT8 *) NULL);} +UINT16 BTM_ReadScoDiscReason (void) {return (BTM_INVALID_SCO_DISC_REASON);} +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) {return (BTM_MODE_UNSUPPORTED);} +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) { return (BTM_ILLEGAL_VALUE);} +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) { return (BTM_MODE_UNSUPPORTED);} +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) { return (BTM_MODE_UNSUPPORTED);} +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) {} +UINT8 BTM_GetNumScoLinks (void) {return (0);} + +#endif /* If SCO is being used */ diff --git a/stack/btm/btm_sec.c b/stack/btm/btm_sec.c new file mode 100644 index 0000000..956364e --- /dev/null +++ b/stack/btm/btm_sec.c @@ -0,0 +1,5649 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Security Manager + * + ******************************************************************************/ + +#include +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" + +#if (BT_USE_TRACES == TRUE && BT_TRACE_VERBOSE == FALSE) +/* needed for sprintf() */ +#include +#endif + +#if BLE_INCLUDED == TRUE + #include "gatt_int.h" +#endif + +#define BTM_SEC_MAX_COLLISION_DELAY (GKI_SECS_TO_TICKS(5)) + +#ifdef APPL_AUTH_WRITE_EXCEPTION +BOOLEAN (APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR bd_addr); +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (BOOLEAN is_originator, UINT16 psm); +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur); +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, + UINT32 mx_chan_id); + +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle); +static void btm_restore_mode(void); +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle); +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state); + +#if (BT_USE_TRACES == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state); +#endif + +static void btm_sec_check_pending_reqs(void); +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +static void btm_sec_bond_cancel_complete (void); +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec); + +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]); + +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason); +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state); + +static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); + +/* TRUE - authenticated link key is possible */ +static const BOOLEAN btm_sec_io_map [BTM_IO_CAP_MAX][BTM_IO_CAP_MAX] = +{ + /* OUT, IO, IN, NONE */ +/* OUT */ {FALSE, FALSE, TRUE, FALSE}, +/* IO */ {FALSE, TRUE, TRUE, FALSE}, +/* IN */ {TRUE, TRUE, TRUE, FALSE}, +/* NONE */ {FALSE, FALSE, FALSE, FALSE} +}; +/* BTM_IO_CAP_OUT 0 DisplayOnly */ +/* BTM_IO_CAP_IO 1 DisplayYesNo */ +/* BTM_IO_CAP_IN 2 KeyboardOnly */ +/* BTM_IO_CAP_NONE 3 NoInputNoOutput */ + +/******************************************************************************* +** +** Function BTM_SecRegister +** +** Description Application manager calls this function to register for +** security services. There can be one and only one application +** saving link keys. BTM allows only first registration. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegister (tBTM_APPL_INFO *p_cb_info) +{ +#if BLE_INCLUDED == TRUE + BT_OCTET16 temp_value = {0}; +#endif + + BTM_TRACE_EVENT0 ("BTM_Sec: application registered"); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_ERROR1 ("BTM_SecRegister:p_cb_info->p_le_callback == 0x%x ", p_cb_info->p_le_callback); + + if (p_cb_info->p_le_callback) + { +#if SMP_INCLUDED == TRUE + BTM_TRACE_EVENT0 ("BTM_Sec: SMP_Register( btm_proc_smp_cback )"); + SMP_Register(btm_proc_smp_cback); +#endif + /* if no IR is loaded, need to regenerate all the keys */ + if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) + { + btm_ble_reset_id(); + } + } + else + { + BTM_TRACE_ERROR0 ("BTM_SecRegister:p_cb_info->p_le_callback == NULL "); + } +#endif + + + + btm_cb.api = *p_cb_info; +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_ERROR1 ("BTM_SecRegister: btm_cb.api.p_le_callback = 0x%x ", btm_cb.api.p_le_callback); +#endif + BTM_TRACE_EVENT0 ("BTM_Sec: application registered"); + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecRegisterLinkKeyNotificationCallback +** +** Description Application manager calls this function to register for +** link key notification. When there is nobody registered +** we should avoid changing link key +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegisterLinkKeyNotificationCallback (tBTM_LINK_KEY_CALLBACK *p_callback) +{ + btm_cb.api.p_link_key_callback = p_callback; + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecAddRmtNameNotifyCallback +** +** Description Any profile can register to be notified when name of the +** remote device is resolved. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i] == NULL) + { + btm_cb.p_rmt_name_callback[i] = p_callback; + return(TRUE); + } + } + + return(FALSE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description Any profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i] == p_callback) + { + btm_cb.p_rmt_name_callback[i] = NULL; + return(TRUE); + } + } + + return(FALSE); +} + + +/******************************************************************************* +** +** Function BTM_SecSetConnectFilterCallback +** +** Description Host can register to be asked whenever a HCI connection +** request is received. In the registered function host +** suppose to check connectibility filters. Yes/No result +** should be returned syncronously +** +** Returns void +** +*******************************************************************************/ +void BTM_SecSetConnectFilterCallback (tBTM_FILTER_CB *p_callback) +{ + btm_cb.p_conn_filter_cb = p_callback; +} + +/******************************************************************************* +** +** Function BTM_GetSecurityMode +** +** Description Get security mode for the device +** +** Returns void +** +*******************************************************************************/ +UINT8 BTM_GetSecurityMode (void) +{ + return(btm_cb.security_mode); +} + +/******************************************************************************* +** +** Function BTM_GetSecurityFlags +** +** Description Get security flags for the device +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 * p_sec_flags) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + *p_sec_flags = p_dev_rec->sec_flags; + return(TRUE); + } + BTM_TRACE_ERROR0 ("BTM_GetSecurityFlags false"); + return(FALSE); +} + +/******************************************************************************* +** +** Function BTM_SetSecurityMode +** +** Description Set security mode for the device +** +** Returns void +** +*******************************************************************************/ +void BTM_SetSecurityMode (UINT8 security_mode) +{ + UINT8 old_mode = btm_cb.security_mode; + + UINT8 sp_mode = HCI_SPD_MODE_ENABLED; + UINT8 sp_debug_mode = HCI_SPD_MODE_DISABLED; + + switch (security_mode) + { +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + case BTM_SEC_MODE_NONE: + case BTM_SEC_MODE_SERVICE: + case BTM_SEC_MODE_LINK: + break; +#endif + + case BTM_SEC_MODE_SP_DEBUG: + sp_debug_mode = HCI_SPD_MODE_ENABLED; + break; + case BTM_SEC_MODE_SP: + /* the default is enabled */ + break; + default: + BTM_TRACE_ERROR1 ("BTM_SetSecurityMode: unknown mode:%d", security_mode); + return; + } + btm_cb.security_mode = security_mode; + + if (HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + /* Lisbon devices and only use BTM_SEC_MODE_SP */ + btm_cb.security_mode = BTM_SEC_MODE_SP; + BTM_TRACE_DEBUG2("BTM_SetSecurityMode: SP:%d, debug:%d", sp_mode, sp_debug_mode); + btsnd_hcic_write_simple_pairing_mode(sp_mode); + btsnd_hcic_write_simp_pair_debug_mode(sp_debug_mode); + return; + } + + /* must be a pre-Lisbon device */ +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + /* If previously security mode was Link Level and now lesser notify */ + /* controller not to perform authentication, encryption on startup */ + if ((old_mode == BTM_SEC_MODE_LINK) + && ( security_mode != BTM_SEC_MODE_LINK)) + { + BTM_TRACE_DEBUG0("BTM_SetSecurityMode: Authen Enable -> FALSE"); + btsnd_hcic_write_auth_enable (FALSE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_DISABLED); + } + + /* If previously security is increased to Link Level notify */ + /* controller to perform authentication, encryption on startup */ + if ((old_mode != BTM_SEC_MODE_LINK) + && ( security_mode == BTM_SEC_MODE_LINK)) + { + BTM_TRACE_DEBUG0("BTM_SetSecurityMode: Authen Enable -> TRUE"); + btsnd_hcic_write_auth_enable (TRUE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_POINT_TO_POINT); + } +#endif /* BTM_PRE_LISBON_INCLUDED == TRUE */ +} + +/******************************************************************************* +** +** Function BTM_SetPinType +** +** Description Set PIN type for the device. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len) +{ + BTM_TRACE_API3 ("BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d", + pin_type, (char *) pin_code, pin_code_len); + + /* If device is not up security mode will be set as a part of startup */ + if ( (btm_cb.cfg.pin_type != pin_type) + && (btm_cb.devcb.state > BTM_DEV_STATE_WAIT_AFTER_RESET) ) + { + btsnd_hcic_write_pin_type (pin_type); + } + + btm_cb.cfg.pin_type = pin_type; + btm_cb.cfg.pin_code_len = pin_code_len; + memcpy (btm_cb.cfg.pin_code, pin_code, pin_code_len); +} + +/******************************************************************************* +** +** Function BTM_SetPairableMode +** +** Description Enable or disable pairing +** +** Parameters allow_pairing - (TRUE or FALSE) whether or not the device +** allows pairing. +** connect_only_paired - (TRUE or FALSE) whether or not to +** only allow paired devices to connect. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_paired) +{ + BTM_TRACE_API2 ("BTM_SetPairableMode() allow_pairing: %u connect_only_paired: %u", allow_pairing, connect_only_paired); + + btm_cb.pairing_disabled = !allow_pairing; + btm_cb.connect_only_paired = connect_only_paired; +} + + +#define BTM_NO_AVAIL_SEC_SERVICES ((UINT16) 0xffff) + +/******************************************************************************* +** +** Function BTM_SetUCDSecurityLevel +** +** Description Register UCD service security level with Security Manager +** +** Parameters: is_originator - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SetUCDSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + CONNECTION_TYPE conn_type; + + if (is_originator) + conn_type = CONNLESS_ORIG; + else + conn_type = CONNLESS_TERM; + + return(btm_sec_set_security_level (conn_type, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_SetSecurityLevel +** +** Description Register service security level with Security Manager +** +** Parameters: is_originator - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + CONNECTION_TYPE conn_type; + + if (is_originator) + conn_type = CONN_ORIENT_ORIG; + else + conn_type = CONN_ORIENT_TERM; + + return(btm_sec_set_security_level (conn_type, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#else + return(btm_sec_set_security_level (is_originator, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#endif +} + +/******************************************************************************* +** +** Function btm_sec_set_security_level +** +** Description Register service security level with Security Manager +** +** Parameters: conn_type - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ + tBTM_SEC_SERV_REC *p_srec; + UINT16 index; + UINT16 first_unused_record = BTM_NO_AVAIL_SEC_SERVICES; + BOOLEAN record_allocated = FALSE; + BOOLEAN is_originator; +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; + + if (conn_type & CONNECTION_TYPE_ORIG_MASK) + is_originator = TRUE; + else + is_originator = FALSE; + + if (conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + { + is_ucd = TRUE; + } + else + { + is_ucd = FALSE; + } +#else + is_originator = conn_type; +#endif + + /* See if the record can be reused (same service name, psm, mx_proto_id, + service_id, and mx_chan_id), or obtain the next unused record */ + + p_srec = &btm_cb.sec_serv_rec[0]; + + + for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) + { + /* Check if there is already a record for this service */ + if (p_srec->security_flags & BTM_SEC_IN_USE) + { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id && + (!strncmp (p_name, (char *) p_srec->orig_service_name, + BTM_SEC_SERVICE_NAME_LEN) || + !strncmp (p_name, (char *) p_srec->term_service_name, + BTM_SEC_SERVICE_NAME_LEN))) +#else + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id) +#endif + { + record_allocated = TRUE; + break; + } + } + /* Mark the first available service record */ + else if (!record_allocated) + { + memset (p_srec, 0, sizeof(tBTM_SEC_SERV_REC)); + record_allocated = TRUE; + first_unused_record = index; + } + } + + if (!record_allocated) + { + BTM_TRACE_WARNING1("BTM_SEC_REG: Out of Service Records (%d)", BTM_SEC_MAX_SERVICE_RECORDS); + return(record_allocated); + } + + /* Process the request if service record is valid */ + /* If a duplicate service wasn't found, use the first available */ + if (index >= BTM_SEC_MAX_SERVICE_RECORDS) + { + index = first_unused_record; + p_srec = &btm_cb.sec_serv_rec[index]; + } + + p_srec->psm = psm; + p_srec->service_id = service_id; + p_srec->mx_proto_id = mx_proto_id; + + if (is_originator) + { + p_srec->orig_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->orig_service_name, sizeof(p_srec->orig_service_name), p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->ucd_security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + + /* Parameter validation. Originator should not set requirements for incoming connections */ + sec_level &= ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (sec_level & BTM_SEC_OUT_AUTHENTICATE) + sec_level |= BTM_SEC_OUT_MITM; + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_OUT_ENCRYPT) + sec_level |= BTM_SEC_OUT_AUTHENTICATE; + + /* outgoing connections usually set the security level right before + * the connection is initiated. + * set it to be the outgoing service */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd == FALSE ) +#endif + { + btm_cb.p_out_serv = p_srec; + } + } + else + { + p_srec->term_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->term_service_name, sizeof(p_srec->term_service_name), p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->ucd_security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + + /* Parameter validation. Acceptor should not set requirements for outgoing connections */ + sec_level &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (sec_level & BTM_SEC_IN_AUTHENTICATE) + sec_level |= BTM_SEC_IN_MITM; + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_IN_ENCRYPT) + sec_level |= BTM_SEC_IN_AUTHENTICATE; + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->security_flags |= (UINT16)(BTM_SEC_IN_USE); + p_srec->ucd_security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } + else + { + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } + + BTM_TRACE_API6("BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, chan_id %d", + index, service_id, conn_type, psm, mx_proto_id, mx_chan_id); + + BTM_TRACE_API2(" : security_flags: 0x%04x, ucd_security_flags: 0x%04x", + p_srec->security_flags, p_srec->ucd_security_flags); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API2(" : service name [%s] (up to %d chars saved)", + p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#else + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + + BTM_TRACE_API6("BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d", + index, service_id, is_originator, psm, mx_proto_id, mx_chan_id); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API3(" : sec: 0x%x, service name [%s] (up to %d chars saved)", + p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#endif + + + return(record_allocated); +} + +/******************************************************************************* +** +** Function BTM_SecClrService +** +** Description Removes specified service record(s) from the security database. +** All service records with the specified name are removed. +** Typically used only by devices with limited RAM so that it can +** reuse an old security service record. +** +** Note: Unpredictable results may occur if a service is cleared +** that is still in use by an application/profile. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +UINT8 BTM_SecClrService (UINT8 service_id) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm != BT_PSM_SDP) && + (!service_id || (service_id == p_srec->service_id))) + { + BTM_TRACE_API2("BTM_SEC_CLR[%d]: id %d", i, service_id); + p_srec->security_flags = 0; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_srec->ucd_security_flags = 0; +#endif + num_freed++; + } + } + + return(num_freed); +} + +/******************************************************************************* +** +** Function btm_sec_clr_service_by_psm +** +** Description Removes specified service record from the security database. +** All service records with the specified psm are removed. +** Typically used by L2CAP to free up the service record used +** by dynamic PSM clients when the channel is closed. +** The given psm must be a virtual psm. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +UINT8 btm_sec_clr_service_by_psm (UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm) ) + { + BTM_TRACE_API2("BTM_SEC_CLR[%d]: id %d ", i, p_srec->service_id); + p_srec->security_flags = 0; + num_freed++; + } + } + BTM_TRACE_API2("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d", psm, num_freed); + + return(num_freed); +} + +/******************************************************************************* +** +** Function BTM_SecClrUCDService +** +** Description +** +** Parameters Service ID - Id of the service to remove. +** ('0' removes all service records ) +** +** Returns Number of records that were cleared. +** +*******************************************************************************/ +UINT8 BTM_SecClrUCDService (UINT8 service_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_cleared = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && + (!service_id || (service_id == (UINT32)p_srec->service_id))) + { + BTM_TRACE_API2("BTM_UCD_SEC_CLR[%d]: id %d", i, service_id); + p_srec->ucd_security_flags = 0; + num_cleared++; + } + } + + return(num_cleared); +#else + return(0); +#endif +} + +/******************************************************************************* +** +** Function BTM_PINCodeReply +** +** Description This function is called after Security Manager submitted +** PIN code request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_API4 ("BTM_PINCodeReply(): PairState: %s PairFlags: 0x%02x PinLen:%d Result:%d", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) + { + BTM_TRACE_WARNING1 ("BTM_PINCodeReply() - Wrong State: %d", btm_cb.pairing_state); + return; + } + + if (memcmp (bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) + { + BTM_TRACE_ERROR0 ("BTM_PINCodeReply() - Wrong BD Addr"); + return; + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + BTM_TRACE_ERROR0 ("BTM_PINCodeReply() - no dev CB"); + return; + } + + if ( (pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL) ) + res = BTM_ILLEGAL_VALUE; + + if (res != BTM_SUCCESS) + { + /* if peer started dd OR we started dd and pre-fetch pin was not used send negative reply */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) || + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) ) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + btsnd_hcic_pin_code_neg_reply (bd_addr); + } + else + { + p_dev_rec->security_required = BTM_SEC_NONE; + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + return; + } + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) + && (btm_cb.security_mode_changed == FALSE) ) + { + /* This is start of the dedicated bonding if local device is 2.0 */ + btm_cb.pin_code_len = pin_len; + memcpy (btm_cb.pin_code, p_pin, pin_len); + + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if(!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + btsnd_hcic_write_auth_enable (TRUE); + + btm_cb.acl_disc_reason = 0xff ; + + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) + { + BTM_TRACE_WARNING0 ("BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected incoming connection"); + /* we change state little bit early so btm_sec_connected() will originate connection */ + /* when existing ACL link is down completely */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + /* if we already accepted incoming connection from pairing device */ + else if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) + { + BTM_TRACE_WARNING0 ("BTM_PINCodeReply(): link is connecting so wait pin code request from peer"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_AUTH_FAILURE); + } + return; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_SUCCESS; + +#ifdef PORCHE_PAIRING_CONFLICT + BTM_TRACE_EVENT2("BTM_PINCodeReply(): Saving pin_len: %d btm_cb.pin_code_len: %d", pin_len, btm_cb.pin_code_len); + /* if this was not pre-fetched, save the PIN */ + if (btm_cb.pin_code_len == 0) + memcpy (btm_cb.pin_code, p_pin, pin_len); + btm_cb.pin_code_len_saved = pin_len; +#endif + btsnd_hcic_pin_code_req_reply (bd_addr, pin_len, p_pin); +} + + +/******************************************************************************* +** +** Function BTM_DeviceAuthorized +** +** Description This function is called after Security Manager submitted +** authorization request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** +*******************************************************************************/ +void BTM_DeviceAuthorized (BD_ADDR bd_addr, UINT8 res, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + BTM_TRACE_WARNING6 ("Security Manager: Attempting Authorization of Unknown Device Address [%02x%02x%02x%02x%02x%02x]", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + return; + } + + BTM_TRACE_EVENT4 ("Security Manager: authorized status:%d State:%d Trusted:%08x %08x", + res, (p_dev_rec) ? p_dev_rec->sec_state : 0, trusted_mask[0], trusted_mask[1]); + + if (res == BTM_SUCCESS) + { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + } + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) + return; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + if (res != BTM_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + return; + } + + if ((res = (UINT8)btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } +} + + + +/******************************************************************************* +** +** Function BTM_SecBond +** +** Description This function is called to perform bonding with peer device. +** If the connection is already up, but not secure, pairing +** is attempted. If already paired BTM_SUCCESS is returned. +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Note: After 2.1 parameters are not used and preserved here not to change API +*******************************************************************************/ +tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS status; +#if SMP_INCLUDED == TRUE + tACL_CONN *p=NULL; + BOOLEAN is_le_slave_role=FALSE; +#endif + BTM_TRACE_API6 ("BTM_SecBond BDA: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + /* Other security process is in progress */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + BTM_TRACE_ERROR1 ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state)); + return(BTM_WRONG_MODE); + } + + + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + BTM_TRACE_DEBUG1 ("before update sec_flags=0x%x", p_dev_rec->sec_flags); + + +#if SMP_INCLUDED == TRUE + p = btm_bda_to_acl(bd_addr); + if (p && p->is_le_link ) + { + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) + { + BTM_TRACE_ERROR1 ("BTM_SecBond: LE already busy in state: %x", p_dev_rec->sec_state ); + return(BTM_WRONG_MODE); + } + + if (p->link_role == BTM_ROLE_SLAVE) + { + is_le_slave_role = TRUE; + BTM_TRACE_DEBUG0 ("LE Link Slave" ); + } + else + { + BTM_TRACE_DEBUG0 ("LE Link Maste" ); + } + } + else + { + BTM_TRACE_DEBUG0 ("No LE Link" ); + } + + if (!is_le_slave_role) + { + /* Finished if connection is active and already paired */ + if ( (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) ) + { + BTM_TRACE_WARNING0("BTM_SecBond -> Already Paired"); + return(BTM_SUCCESS); + } + } +#else + /* Finished if connection is active and already paired */ + if ( (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) ) + { + BTM_TRACE_WARNING0("BTM_SecBond -> Already Paired"); + return(BTM_SUCCESS); + } +#endif + + /* Tell controller to get rid of the link key if it has one stored */ + if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS) + return(BTM_NO_RESOURCES); + + /* Save the PIN code if we got a valid one */ + if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) + { + btm_cb.pin_code_len = pin_len; + memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN); + } + + memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; + + p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->is_originator = TRUE; + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + + +#if SMP_INCLUDED == TRUE + + if (!is_le_slave_role) + { + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); + + } + + /* LE device, do SMP pairing */ + if (p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + if (SMP_Pair(p_dev_rec->bd_addr) == SMP_STARTED) + { + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + return BTM_CMD_STARTED; + } + else + return(BTM_NO_RESOURCES); + } +#else + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); +#endif + + BTM_TRACE_DEBUG1 ("after update sec_flags=0x%x", p_dev_rec->sec_flags); + if (!HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + /* The special case when we authenticate keyboard. Set pin type to fixed */ + /* It would be probably better to do it from the application, but it is */ + /* complicated */ + if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) + && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) + { + btm_cb.pin_type_changed = TRUE; + btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED); + } + } + + BTM_TRACE_EVENT1("BTM_SecBond: Local device supports SSP=%d", HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)); + + BTM_TRACE_EVENT4(" remote_features=%02x-%02x-%02x-%02x", + p_dev_rec->features[0], p_dev_rec->features[1], p_dev_rec->features[2], p_dev_rec->features[3]); + BTM_TRACE_EVENT4(" %02x-%02x-%02x-%02x", + p_dev_rec->features[4], p_dev_rec->features[5], p_dev_rec->features[6], p_dev_rec->features[7]); + + BTM_TRACE_EVENT2 ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle); + +#if BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE + p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN; +#endif + + /* If connection already exists... */ + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + { + if (!btm_sec_start_authentication (p_dev_rec)) + return(BTM_NO_RESOURCES); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + + /* Mark lcb as bonding */ + l2cu_update_lcb_4_bonding (bd_addr, TRUE); + return(BTM_CMD_STARTED); + } + + BTM_TRACE_DEBUG2 ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4); + if (!HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features) + || (p_dev_rec->sm4 == BTM_SM4_KNOWN)) + { + if ( btm_sec_check_prefetch_pin (p_dev_rec) ) + return(BTM_CMD_STARTED); + } + if (BTM_SEC_MODE_SP == btm_cb.security_mode && BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* local is 2.1 and peer is unknown */ + if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) + { + /* we are not accepting connection request from peer + * -> RNR (to learn if peer is 2.1) + * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bd_addr, NULL); + } + else + { + /* We are accepting connection request from peer */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + BTM_TRACE_DEBUG3 ("State:%s sm4: 0x%x sec_state:%d", btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state); + return BTM_CMD_STARTED; + } + + /* both local and peer are 2.1 */ + status = btm_sec_dd_create_conn(p_dev_rec); + + if (status != BTM_CMD_STARTED) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + return status; +} + + +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Parameters: bd_addr - Address of the peer device +** +*******************************************************************************/ +tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if SMP_INCLUDED == TRUE + tACL_CONN *p=NULL; +#endif + + BTM_TRACE_API2 ("BTM_SecBondCancel() State: %s flags:0x%x", + btm_pair_state_descr (btm_cb.pairing_state), btm_cb.pairing_flags); + + if (((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return BTM_UNKNOWN_ADDR; + +#if SMP_INCLUDED == TRUE + p = btm_bda_to_acl(bd_addr); + if (p && p->is_le_link && + (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING)) + { + BTM_TRACE_DEBUG0 ("Cancel LE pairing"); + if (SMP_PairCancel(bd_addr)) + { + return BTM_CMD_STARTED; + } + else + { + return BTM_WRONG_MODE; + } + } + +#endif + BTM_TRACE_DEBUG2 ("hci_handle:0x%x sec_state:%d", p_dev_rec->hci_handle, p_dev_rec->sec_state ); + if (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) + { + /* pre-fetching pin for dedicated bonding */ + btm_sec_bond_cancel_complete(); + return BTM_SUCCESS; + } + + /* If this BDA is in a bonding procedure */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) + { + /* If the HCI link is up */ + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + { + /* If some other thread disconnecting, we do not send second command */ + if (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING) + return(BTM_CMD_STARTED); + + /* If the HCI link was set up by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + return btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER); + else + l2cu_update_lcb_4_bonding(bd_addr, FALSE); + + return BTM_NOT_AUTHORIZED; + } + else /*HCI link is not up */ + { + /* If the HCI link creation was started by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + { + if (btsnd_hcic_create_conn_cancel(bd_addr)) + return BTM_CMD_STARTED; + + return BTM_NO_RESOURCES; + } + + return BTM_NOT_AUTHORIZED; + } + } + + return BTM_WRONG_MODE; +} + +/******************************************************************************* +** +** Function BTM_SecUseMasterLinkKey +** +** Description This function is called to tell master of the piconet to +** switch to master link key +** +** Parameters: use_master_key - If true Master Link Key shoul be used +** +*******************************************************************************/ +tBTM_STATUS BTM_SecUseMasterLinkKey (BOOLEAN use_master_key) +{ + return(btsnd_hcic_master_link_key (use_master_key) ? BTM_SUCCESS : + BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function BTM_SetMasterKeyCompCback +** +** Description This function is called to register for the master key complete +** status event. +** +** Parameters: mkey_cback - callback registered with the security manager +** +*******************************************************************************/ +void BTM_SetMasterKeyCompCback( tBTM_MKEY_CALLBACK *mkey_cback ) +{ + btm_cb.mkey_cback = mkey_cback; +} + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKey +** +** Description This function is called to obtain link key for the device +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Parameters: bd_addr - Address of the device +** link_key - Link Key is copied into this array +** +*******************************************************************************/ +tBTM_STATUS BTM_SecGetDeviceLinkKey (BD_ADDR bd_addr, LINK_KEY link_key) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) + { + memcpy (link_key, p_dev_rec->link_key, LINK_KEY_LEN); + return(BTM_SUCCESS); + } + return(BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function BTM_SetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed. Can be set to NULL +** if status is not desired. +** p_ref_data - pointer to any data the caller wishes to receive +** in the callback function upon completion. +* can be set to NULL if not used. +** +** Returns BTM_SUCCESS - already encrypted +** BTM_PENDING - command will be returned in the callback +** BTM_WRONG_MODE- connection not up. +** BTM_BUSY - security procedures are currently active +** BTM_MODE_UNSUPPORTED - if security manager not linked in. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBTM_SEC_CBACK *p_callback, + void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS rc; + +#if BLE_INCLUDED == TRUE + tACL_CONN *p; + p = btm_bda_to_acl(bd_addr); +#endif + + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) + { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING0 ("Security Manager: BTM_SetEncryption not connected"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_WRONG_MODE); + + return(BTM_WRONG_MODE); + } + + + if ( +#if BLE_INCLUDED == TRUE + !p->is_le_link && +#endif + (p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) + == (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) + { + BTM_TRACE_EVENT0 ("Security Manager: BTM_SetEncryption already encrypted"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + + if (p_dev_rec->p_callback) + { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING0 ("Security Manager: BTM_SetEncryption busy"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_BUSY); + + return(BTM_BUSY); + } + + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + p_dev_rec->is_originator = FALSE; + + BTM_TRACE_API4 ("Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x Required:0x%x", + p_dev_rec->hci_handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, + p_dev_rec->security_required); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p->is_le_link) + { + rc = btm_ble_set_encryption(bd_addr, p_ref_data, p->link_role); + } + else +#endif + + rc = btm_sec_execute_procedure (p_dev_rec); + + if ( rc != BTM_CMD_STARTED) + { + if (p_callback) + { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, p_dev_rec->p_ref_data, rc); + } + } + return(rc); +} + +/******************************************************************************* + * disconnect the ACL link, if it's not done yet. +*******************************************************************************/ +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason) +{ + UINT8 old_state = p_dev_rec->sec_state; + + BTM_TRACE_EVENT2 ("btm_sec_send_hci_disconnect: handle:0x%x, reason=0x%x", + p_dev_rec->hci_handle, reason); + + /* if some other thread disconnecting, we do not send second command */ + if (BTM_SEC_STATE_DISCONNECTING != old_state) + { + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING; + +#if BTM_DISC_DURING_RS == TRUE + /* If a Role Switch is in progress, delay the HCI Disconnect to avoid controller problem (4329B1) */ + if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING) + { + BTM_TRACE_ERROR0("RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect to delay disconnect"); + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + return BTM_SUCCESS; + } +#endif + /* Tear down the HCI link */ + if (!btsnd_hcic_disconnect (p_dev_rec->hci_handle, reason)) + { + /* could not send disconnect. restore old state */ + p_dev_rec->sec_state = old_state; + return(BTM_NO_RESOURCES); + } + } + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function BTM_ConfirmReqReply +** +** Description This function is called to confirm the numeric value for +** Simple Pairing in response to BTM_SP_CFM_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** +*******************************************************************************/ +void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_EVENT2 ("BTM_ConfirmReqReply() State: %s Res: %u", + btm_pair_state_descr(btm_cb.pairing_state), res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if ( (res == BTM_SUCCESS) || (res == BTM_SUCCESS_NO_SECURITY) ) + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + + if (res == BTM_SUCCESS) + { + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + } + + btsnd_hcic_user_conf_reply (bd_addr, TRUE); + } + else + { + /* Report authentication failed event from state BTM_PAIR_STATE_WAIT_AUTH_COMPLETE */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_conf_reply (bd_addr, FALSE); + } +} + +/******************************************************************************* +** +** Function BTM_PasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTM_SP_KEY_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) +void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_API2 ("BTM_PasskeyReqReply: State: %s res:%d", + btm_pair_state_descr(btm_cb.pairing_state), res); + + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + { + return; + } + + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) && (res != BTM_SUCCESS) ) + { + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + else + BTM_SecBondCancel(bd_addr); + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_AUTHED | BTM_SEC_LINK_KEY_KNOWN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + return; + } + } + else if (btm_cb.pairing_state != BTM_PAIR_STATE_KEY_ENTRY) + return; + + if (passkey > BTM_MAX_PASSKEY_VAL) + res = BTM_ILLEGAL_VALUE; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_passkey_neg_reply (bd_addr); + } + else + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_user_passkey_reply (bd_addr, passkey); + } +} +#endif + +/******************************************************************************* +** +** Function BTM_SendKeypressNotif +** +** Description This function is used during the passkey entry model +** by a device with KeyboardOnly IO capabilities +** (very likely to be a HID Device). +** It is called by a HID Device to inform the remote device when +** a key has been entered or erased. +** +** Parameters: bd_addr - Address of the peer device +** type - notification type +** +*******************************************************************************/ +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) +void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type) +{ + /* This API only make sense between PASSKEY_REQ and SP complete */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_KEY_ENTRY) + btsnd_hcic_send_keypress_notif (bd_addr, type); +} +#endif + +#if BTM_OOB_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_IoCapRsp +** +** Description This function is called in response to BTM_SP_IO_REQ_EVT +** When the event data io_req.oob_data is set to BTM_OOB_UNKNOWN +** by the tBTM_SP_CALLBACK implementation, this function is +** called to provide the actual response +** +** Parameters: bd_addr - Address of the peer device +** io_cap - The IO capability of local device. +** oob - BTM_OOB_NONE or BTM_OOB_PRESENT. +** auth_req- MITM protection required or not. +** +*******************************************************************************/ +void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req) +{ + BTM_TRACE_EVENT3 ("BTM_IoCapRsp: state: %s oob: %d io_cap: %d", + btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap); + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return; + + if (oob < BTM_OOB_UNKNOWN && io_cap < BTM_IO_CAP_MAX) + { + btm_cb.devcb.loc_auth_req = auth_req; + btm_cb.devcb.loc_io_caps = io_cap; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + auth_req = (BTM_AUTH_DD_BOND | (auth_req&BTM_AUTH_YN_BIT)); + + btsnd_hcic_io_cap_req_reply (bd_addr, io_cap, oob, auth_req); + } +} + +/******************************************************************************* +** +** Function BTM_ReadLocalOobData +** +** Description This function is called to read the local OOB data from +** LM +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalOobData(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + + if (btsnd_hcic_read_local_oob_data() == FALSE) + status = BTM_NO_RESOURCES; + + return status; +} + +/******************************************************************************* +** +** Function BTM_RemoteOobDataReply +** +** Description This function is called to provide the remote OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, BT_OCTET16 c, BT_OCTET16 r) +{ + BTM_TRACE_EVENT2 ("BTM_RemoteOobDataReply(): State: %s res:%d", + btm_pair_state_descr(btm_cb.pairing_state), res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP) + return; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (bd_addr); + } + else + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_rem_oob_reply (bd_addr, c, r); + } +} + +/******************************************************************************* +** +** Function BTM_BuildOobData +** +** Description This function is called to build the OOB data payload to +** be sent over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** max_len - p_data size. +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** name_len- 0, local device name would not be included. +** otherwise, the local device name is included for +** up to this specified length +** +** Returns Number of bytes in p_data. +** +*******************************************************************************/ +UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, + BT_OCTET16 r, UINT8 name_len) +{ + UINT8 *p = p_data; + UINT16 len = 0; + UINT16 delta; +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + UINT16 name_size; + UINT8 name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE; +#endif + + if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) + { + /* add mandatory part */ + UINT16_TO_STREAM(p, len); + BDADDR_TO_STREAM(p, btm_cb.devcb.local_addr); + + len = BTM_OOB_MANDATORY_SIZE; + max_len -= len; + + /* now optional part */ + + /* add Hash C */ + delta = BTM_OOB_HASH_C_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_HASH_C_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE; + ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE); + len += delta; + max_len -= delta; + } + + /* add Rand R */ + delta = BTM_OOB_RAND_R_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_RAND_R_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE; + ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE); + len += delta; + max_len -= delta; + } + + /* add class of device */ + delta = BTM_OOB_COD_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_COD_SIZE + 1; + *p++ = BTM_EIR_OOB_COD_TYPE; + DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class); + len += delta; + max_len -= delta; + } +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + name_size = name_len; + if (name_size > strlen(btm_cb.cfg.bd_name)) + { + name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; + name_size = (UINT16)strlen(btm_cb.cfg.bd_name); + } + delta = name_size + 2; + if (max_len >= delta) + { + *p++ = name_size + 1; + *p++ = name_type; + ARRAY_TO_STREAM (p, btm_cb.cfg.bd_name, name_size); + len += delta; + max_len -= delta; + } +#endif + /* update len */ + p = p_data; + UINT16_TO_STREAM(p, len); + } + return len; +} + +/******************************************************************************* +** +** Function BTM_ReadOobData +** +** Description This function is called to parse the OOB data payload +** received over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** eir_tag - The associated EIR tag to read the data. +** *p_len(output) - the length of the data with the given tag. +** +** Returns the beginning of the data with the given tag. +** NULL, if the tag is not found. +** +*******************************************************************************/ +UINT8 * BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len) +{ + UINT8 *p = p_data; + UINT16 max_len; + UINT8 len, type; + UINT8 *p_ret = NULL; + UINT8 ret_len = 0; + + if (p_data) + { + STREAM_TO_UINT16(max_len, p); + if (max_len >= BTM_OOB_MANDATORY_SIZE) + { + if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) + { + p_ret = p; /* the location for bd_addr */ + ret_len = BTM_OOB_BD_ADDR_SIZE; + } + else + { + p += BD_ADDR_LEN; + max_len -= BTM_OOB_MANDATORY_SIZE; + /* now the optional data in EIR format */ + while (max_len > 0) + { + len = *p++; /* tag data len + 1 */ + type = *p++; + if (eir_tag == type) + { + p_ret = p; + ret_len = len - 1; + break; + } + /* the data size of this tag is len + 1 (tag data len + 2) */ + if (max_len > len) + { + max_len -= len; + max_len--; + len--; + p += len; + } + else + max_len = 0; + } + } + } + } + + if (p_len) + *p_len = ret_len; + + return p_ret; +} +#endif + +/******************************************************************************* +** +** Function BTM_SetOutService +** +** Description This function is called to set the service for +** outgoing connections. +** +** If the profile/application calls BTM_SetSecurityLevel +** before initiating a connection, this function does not +** need to be called. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + btm_cb.p_out_serv = p_serv_rec; + p_dev_rec = btm_find_dev (bd_addr); + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->service_id == service_id) + && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) + { + BTM_TRACE_API4("BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, chan_id %d", + p_serv_rec->service_id, p_serv_rec->psm, p_serv_rec->mx_proto_id, p_serv_rec->orig_mx_chan_id); + btm_cb.p_out_serv = p_serv_rec; + if (p_dev_rec) + p_dev_rec->p_cur_service = p_serv_rec; + break; + } + } +} + +/************************************************************************ +** I N T E R N A L F U N C T I O N S +*************************************************************************/ +/******************************************************************************* +** +** Function btm_sec_check_upgrade +** +** Description This function is called to check if the existing link key +** needs to be upgraded. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + tBTM_SP_UPGRADE evt_data; + UINT16 mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM; + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + { + + BTM_TRACE_DEBUG5 ("btm_sec_check_upgrade id:%d, link_key_typet:%d, rmt_io_caps:%d, chk flags:x%x, flags:x%x", + p_dev_rec->p_cur_service->service_id, p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, + mtm_check, p_dev_rec->p_cur_service->security_flags); + /* Already have a link key to the connected peer. Is the link key secure enough? + ** Is a link key upgrade even possible? + */ + if ((p_dev_rec->security_required & mtm_check) /* needs MITM */ + && (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB) /* has unauthenticated link key */ + && (p_dev_rec->rmt_io_caps < BTM_IO_CAP_MAX) /* a valid peer IO cap */ + && (btm_sec_io_map[p_dev_rec->rmt_io_caps][btm_cb.devcb.loc_io_caps])) /* authenticated link key is possible */ + { + BTM_TRACE_DEBUG1 ("need upgrade!! sec_flags:0x%x", p_dev_rec->sec_flags); + /* upgrade is possible: check if the application wants the upgrade. + * If the application is configured to use a global MITM flag, + * it probably would not want to upgrade the link key based on the security level database */ + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + evt_data.upgrade = TRUE; + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_UPGRADE_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + + BTM_TRACE_DEBUG1 ("evt_data.upgrade:0x%x", evt_data.upgrade); + if (evt_data.upgrade) + { + /* if the application confirms the upgrade, set the upgrade bit */ + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + + /* Clear the link key known to go through authentication/pairing again */ + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; + BTM_TRACE_DEBUG1 ("sec_flags:0x%x", p_dev_rec->sec_flags); + } + } + } +} + +/******************************************************************************* +** +** Function btm_sec_l2cap_access_req +** +** Description This function is called by the L2CAP to grant permission to +** establish L2CAP connection to or from the peer device. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are complete. MUST NOT BE NULL. +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +#define BTM_SEC_OUT_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHORIZE) +#define BTM_SEC_IN_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE) + +tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle, + CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, + void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + UINT16 security_required; + UINT16 old_security_required; + BOOLEAN old_is_originator; + tBTM_STATUS rc = BTM_SUCCESS; + BOOLEAN chk_acp_auth_done = FALSE; + BOOLEAN is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if (conn_type & CONNECTION_TYPE_ORIG_MASK) + is_originator = TRUE; + else + is_originator = FALSE; + + BTM_TRACE_DEBUG2 ("btm_sec_l2cap_access_req conn_type:0x%x, 0x%x", conn_type, p_ref_data); +#else + is_originator = conn_type; + + BTM_TRACE_DEBUG2 ("btm_sec_l2cap_access_req is_originator:%d, 0x%x", is_originator, p_ref_data); +#endif + + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + p_dev_rec->hci_handle = handle; + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_first_serv (conn_type, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + BTM_TRACE_WARNING1 ("btm_sec_l2cap_access_req() PSM:%d no application registerd", psm); + + (*p_callback) (bd_addr, p_ref_data, BTM_MODE_UNSUPPORTED); + + return(BTM_MODE_UNSUPPORTED); + } + + /* SDP connection we will always let through */ + if (BT_PSM_SDP == psm) + { + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS_NO_SECURITY); + + return(BTM_SUCCESS); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + { + security_required = p_serv_rec->ucd_security_flags; + + rc = BTM_CMD_STARTED; + if (is_originator) + { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + else + { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + + if (rc == BTM_SUCCESS) + { + if (p_callback) + (*p_callback) (bd_addr, (void *)p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + else +#endif + { + security_required = p_serv_rec->security_flags; + } + + /* there are some devices (moto KRZR) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) + { + BTM_TRACE_EVENT2 ("btm_sec_l2cap_access_req() - busy - PSM:%d delayed state: %s", + psm, btm_pair_state_descr(btm_cb.pairing_state)); + rc = BTM_CMD_STARTED; + if ((BTM_SEC_MODE_SP != btm_cb.security_mode) + || ((BTM_SEC_MODE_SP == btm_cb.security_mode) && (BTM_SM4_KNOWN == p_dev_rec->sm4)) + ) + { + BTM_TRACE_EVENT2 ("security_flags:x%x, sec_flags:x%x", security_required, p_dev_rec->sec_flags); + /* legacy mode - local is legacy or local is lisbon/peer is legacy */ + if (is_originator) + { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + else + { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + + if (rc == BTM_SUCCESS) + { + if (p_callback) + (*p_callback) (bd_addr, (void *)p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + + btm_cb.sec_req_pending = TRUE; + return(BTM_CMD_STARTED); + } + + /* Save pointer to service record */ + p_dev_rec->p_cur_service = p_serv_rec; + + + /* mess /w security_required in btm_sec_l2cap_access_req for Lisbon */ + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (is_originator) + { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); + } + else + { + if ( !(BTM_SM4_KNOWN & p_dev_rec->sm4)) + { + BTM_TRACE_DEBUG1 ("remote features unknown!!sec_flags:0x%x", p_dev_rec->sec_flags); + /* the remote features are not known yet */ + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + + return(BTM_CMD_STARTED); + } + } + } + else + { + /* responder */ + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* SM4 to SM4: the acceptor needs to make sure the authentication is already done */ + chk_acp_auth_done = TRUE; + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + } + else + { + if ( !(BTM_SM4_KNOWN & p_dev_rec->sm4)) + { + BTM_TRACE_DEBUG1 ("(rsp) remote features unknown!!sec_flags:0x%x", p_dev_rec->sec_flags); + /* the remote features are not known yet */ + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + + return(BTM_CMD_STARTED); + } + } + } + } + + BTM_TRACE_DEBUG4 ("btm_sec_l2cap_access_req() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", + p_dev_rec->sm4, p_dev_rec->sec_flags, security_required, chk_acp_auth_done); + + old_security_required = p_dev_rec->security_required; + old_is_originator = p_dev_rec->is_originator; + p_dev_rec->security_required = security_required; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->is_originator = is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + p_dev_rec->is_ucd = TRUE; + else + p_dev_rec->is_ucd = FALSE; +#endif + + /* If there are multiple service records used through the same PSM */ + /* leave security decision for the multiplexor on the top */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if (((btm_sec_find_next_serv (p_serv_rec)) != NULL) + &&(!( conn_type & CONNECTION_TYPE_CONNLESS_MASK ))) /* if not UCD */ +#else + if ((btm_sec_find_next_serv (p_serv_rec)) != NULL) +#endif + { + BTM_TRACE_DEBUG2 ("no next_serv sm4:0x%x, chk:%d", p_dev_rec->sm4, chk_acp_auth_done); + if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + BTM_TRACE_EVENT1 ("Security Manager: l2cap_access_req PSM:%d postponed for multiplexer", psm); + /* pre-Lisbon: restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + + /* if the originator is using dynamic PSM in legacy mode, do not start any security process now. + * The layer above L2CAP needs to carry out the security requirement after L2CAP connect response is received*/ + if (is_originator && (btm_cb.security_mode != BTM_SEC_MODE_SP || !BTM_SEC_IS_SM4(p_dev_rec->sm4)) && (psm >= 0x1001)) + { + BTM_TRACE_EVENT1 ("dynamic PSM:0x%x in legacy mode - postponed for upper layer", psm); + /* restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + + if (chk_acp_auth_done) + { + BTM_TRACE_DEBUG2 ("(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: x%x", + (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED), (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)); + /* SM4, but we do not know for sure which level of security we need. + * as long as we have a link key, it's OK */ + if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + ||(0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) + { + rc = BTM_DELAY_CHECK; + /* + 2046 may report HCI_Encryption_Change and L2C Connection Request out of sequence + because of data path issues. Delay this disconnect a little bit + */ + BTM_TRACE_ERROR0 ("peer should have initiated security process by now (SM4 to SM4)"); + p_dev_rec->p_callback = p_callback; + p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC; + (*p_callback) (bd_addr, p_ref_data, rc); + + return(BTM_SUCCESS); + } + } + + p_dev_rec->p_callback = p_callback; + + /* Although authentication and encryption are per connection */ + /* authorization is per access request. For example when serial connection */ + /* is up and authorized and client requests to read file (access to other */ + /* scn, we need to request user's permission again. */ + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED; + + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* If we already have a link key to the connected peer, is the link key secure enough ? */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + + BTM_TRACE_EVENT6 ("Security Manager: l2cap_access_req PSM:%d Handle:%d State:%d Flags:0x%x Required:0x%x Service ID:%d", + psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, p_dev_rec->p_ref_data, (UINT8)rc); + } + + return(rc); +} + +/******************************************************************************* +** +** Function btm_sec_mx_access_request +** +** Description This function is called by all Multiplexing Protocols during +** establishing connection to or from peer device to grant +** permission to establish application connection. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** mx_proto_id - protocol ID of the multiplexer +** mx_chan_id - multiplexer channel to reach application +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed +** p_ref_data - Pointer to any reference data needed by the +** the callback function. +** +** Returns BTM_CMD_STARTED +** +*******************************************************************************/ +tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) + +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + tBTM_STATUS rc; + + BTM_TRACE_DEBUG1 ("btm_sec_mx_access_request is_originator:%d", is_originator); + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_mx_serv (is_originator, psm, mx_proto_id, mx_chan_id); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_MODE_UNSUPPORTED); + + BTM_TRACE_ERROR3 ("Security Manager: MX service not found PSM:%d Proto:%d SCN:%d", + psm, mx_proto_id, mx_chan_id); + return BTM_NO_RESOURCES; + } + + /* there are some devices (moto phone) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) + { + BTM_TRACE_EVENT4 ("btm_sec_mx_access_request service PSM:%d Proto:%d SCN:%d delayed state: %s", + psm, mx_proto_id, mx_chan_id, btm_pair_state_descr(btm_cb.pairing_state)); + + btm_sec_queue_mx_request (bd_addr, psm, is_originator, mx_proto_id, mx_chan_id, p_callback, p_ref_data); + return BTM_CMD_STARTED; + } + + p_dev_rec->p_cur_service = p_serv_rec; + p_dev_rec->security_required = p_serv_rec->security_flags; + + if (BTM_SEC_MODE_SP == btm_cb.security_mode) + { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* If we already have a link key, check if that link key is good enough */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + } + + p_dev_rec->is_originator = is_originator; + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + + /* Although authentication and encryption are per connection */ + /* authorization is per access request. For example when serial connection */ + /* is up and authorized and client requests to read file (access to other */ + /* scn, we need to request user's permission again. */ + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED); + + BTM_TRACE_EVENT6 ("Security Manager: mx_access_req proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service ID:%d", + mx_proto_id, mx_chan_id, p_dev_rec->sec_state, p_dev_rec->sec_flags, p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + if (p_callback) + { + p_dev_rec->p_callback = NULL; + + (*p_callback) (bd_addr, p_ref_data, (UINT8)rc); + } + } + + return rc; +} + +/******************************************************************************* +** +** Function btm_sec_conn_req +** +** Description This function is when the peer device is requesting +** connection +** +** Returns void +** +*******************************************************************************/ +void btm_sec_conn_req (UINT8 *bda, UINT8 *dc) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + + /* Some device may request a connection before we are done with the HCI_Reset sequence */ + if (btm_cb.devcb.state != BTM_DEV_STATE_READY) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request when device not ready"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Security guys wants us not to allow connection from not paired devices */ + + /* Check if connection is allowed for only paired devices */ + if (btm_cb.connect_only_paired) + { + if (!p_dev_rec || !(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request from non-paired device"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } + +#if BTM_ALLOW_CONN_IF_NONDISCOVER == FALSE + /* If non-discoverable, only allow known devices to connect */ + if (btm_cb.btm_inq_vars.discoverable_mode == BTM_NON_DISCOVERABLE) + { + if (!p_dev_rec) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request from not paired device"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } +#endif + + /* Host can be registered to verify comming BDA or DC */ + if (btm_cb.p_conn_filter_cb) + { + if (!(* btm_cb.p_conn_filter_cb) (bda, dc)) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request did not pass filter"); + + /* incomming call did not pass connection filters. Reject */ + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } + + if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + &&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + &&(!memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN))) + { + BTM_TRACE_EVENT0 ("Security Manager: reject connect request from bonding device"); + + /* incoming connection from bonding device is rejected */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_REJECTED_CONNECT; + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Host is not interested or approved connection. Save BDA and DC and */ + /* pass request to L2CAP */ + memcpy (btm_cb.connecting_bda, bda, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, dc, DEV_CLASS_LEN); + + if (l2c_link_hci_conn_req (bda)) + { + if (!p_dev_rec) + { + /* accept the connection -> allocate a device record */ + p_dev_rec = btm_sec_alloc_dev (bda); + } + if (p_dev_rec) + { + p_dev_rec->sm4 |= BTM_SM4_CONN_PEND; + } + } +} + +/******************************************************************************* +** +** Function btm_sec_bond_cancel_complete +** +** Description This function is called to report bond cancel complete +** event. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_bond_cancel_complete (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) || + (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags)) + { + /* for dedicated bonding in legacy mode, authentication happens at "link level" + * btm_sec_connected is called with failed status. + * In theory, the code that handles is_pairing_device/TRUE should clean out security related code. + * However, this function may clean out the security related flags and btm_sec_connected would not know + * this function also needs to do proper clean up. + */ + if ((p_dev_rec = btm_find_dev (btm_cb.pairing_bda)) != NULL) + p_dev_rec->security_required = BTM_SEC_NONE; + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* Notify application that the cancel succeeded */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS); + } +} + +/******************************************************************************* +** +** Function btm_create_conn_cancel_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the create connection cancel +** command. +** +** Returns void +** +*******************************************************************************/ +void btm_create_conn_cancel_complete (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + BTM_TRACE_EVENT2 ("btm_create_conn_cancel_complete(): in State: %s status:%d", + btm_pair_state_descr(btm_cb.pairing_state), status); + + /* if the create conn cancel cmd was issued by the bond cancel, + ** the application needs to be notified that bond cancel succeeded + */ + switch (status) + { + case HCI_SUCCESS: + btm_sec_bond_cancel_complete(); + break; + case HCI_ERR_CONNECTION_EXISTS: + case HCI_ERR_NO_CONNECTION: + default: + /* Notify application of the error */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_ERR_PROCESSING); + break; + } +} + +/******************************************************************************* +** +** Function btm_sec_check_pending_reqs +** +** Description This function is called at the end of the security procedure +** to let L2CAP and RFCOMM know to re-submit any pending requests +** +** Returns void +** +*******************************************************************************/ +void btm_sec_check_pending_reqs (void) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + BUFFER_Q bq; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + /* First, resubmit L2CAP requests */ + if (btm_cb.sec_req_pending) + { + btm_cb.sec_req_pending = FALSE; + l2cu_resubmit_pending_sec_req (NULL); + } + + /* Now, re-submit anything in the mux queue */ + bq = btm_cb.sec_pending_q; + + GKI_init_q (&btm_cb.sec_pending_q); + + while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)GKI_dequeue (&bq)) != NULL) + { + /* Check that the ACL is still up before starting security procedures */ + if (btm_bda_to_acl(p_e->bd_addr) != NULL) + { + BTM_TRACE_EVENT4 ("btm_sec_check_pending_reqs() submitting PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u", + p_e->psm, p_e->is_orig, p_e->mx_proto_id, p_e->mx_chan_id); + + btm_sec_mx_access_request (p_e->bd_addr, p_e->psm, p_e->is_orig, + p_e->mx_proto_id, p_e->mx_chan_id, + p_e->p_callback, p_e->p_ref_data); + } + + GKI_freebuf (p_e); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_init +** +** Description This function is on the SEC startup +** +** Returns void +** +*******************************************************************************/ +void btm_sec_init (UINT8 sec_mode) +{ +#if 0 /* cleared in btm_init; put back in if calling from anywhere else! */ + int i; + + memset (btm_cb.sec_serv_rec, 0, sizeof (btm_cb.sec_serv_rec)); + memset (btm_cb.sec_dev_rec, 0, sizeof (btm_cb.sec_dev_rec)); + memset (&btm_cb.pairing_tle, 0, sizeof(TIMER_LIST_ENT)); + +#endif + btm_cb.security_mode = sec_mode; + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.max_collision_delay = BTM_SEC_MAX_COLLISION_DELAY; +} + +/******************************************************************************* +** +** Function btm_sec_device_down +** +** Description This function should be called when device is disabled or +** turned off +** +** Returns void +** +*******************************************************************************/ +void btm_sec_device_down (void) +{ + BTM_TRACE_EVENT1 ("btm_sec_device_down() State: %s", btm_pair_state_descr(btm_cb.pairing_state)); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); +} + +/******************************************************************************* +** +** Function btm_sec_dev_reset +** +** Description This function should be called after device reset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_dev_reset (void) +{ +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) + { + btsnd_hcic_write_auth_enable (TRUE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_POINT_TO_POINT); + } +#endif +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + else +#endif + /* this function is only called from btm_read_local_features_complete() + * right now. */ + if (HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + btsnd_hcic_write_simple_pairing_mode(HCI_SP_MODE_ENABLED); +#if BLE_INCLUDED == TRUE + btsnd_hcic_set_event_mask(LOCAL_BR_EDR_CONTROLLER_ID, + (UINT8 *)HCI_DUMO_EVENT_MASK_EXT); +#else + btsnd_hcic_set_event_mask(LOCAL_BR_EDR_CONTROLLER_ID, + (UINT8 *)HCI_LISBON_EVENT_MASK_EXT); +#endif + /* set the default IO capabilities */ + btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS; + /* add mx service to use no security */ +#if (RFCOMM_INCLUDED == TRUE) + BTM_SetSecurityLevel(FALSE, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, + BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0); +#endif + } + else + { + btm_cb.security_mode = BTM_SEC_MODE_SERVICE; + } + + BTM_TRACE_DEBUG1 ("btm_sec_dev_reset sec mode: %d", btm_cb.security_mode); +} + +/******************************************************************************* +** +** Function btm_sec_abort_access_req +** +** Description This function is called by the L2CAP or RFCOMM to abort +** the pending operation. +** +** Parameters: bd_addr - Address of the peer device +** +** Returns void +** +*******************************************************************************/ +void btm_sec_abort_access_req (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) + return; + + if (btm_cb.api.p_abort_callback) + (*btm_cb.api.p_abort_callback)(bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + + if ((p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) + && (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING)) + return; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; +} + +/******************************************************************************* +** +** Function btm_sec_dd_create_conn +** +** Description This function is called to create the ACL connection for +** the dedicated boding process +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec) +{ + tL2C_LCB *p_lcb; + + /* Make sure an L2cap link control block is available */ + if ((p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE)) == NULL) + { + BTM_TRACE_WARNING6 ("Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + return(BTM_NO_RESOURCES); + } + + /* set up the control block to indicated dedicated bonding */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + + if (l2cu_create_conn(p_lcb) == FALSE) + { + BTM_TRACE_WARNING6 ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + l2cu_release_lcb(p_lcb); + return(BTM_NO_RESOURCES); + } + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); +#endif + + BTM_TRACE_DEBUG6 ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_sec_rmt_name_request_complete +** +** Description This function is called when remote name was obtained from +** the peer device +** +** Returns void +** +*******************************************************************************/ +void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + DEV_CLASS dev_class; + UINT8 old_sec_state; + + BTM_TRACE_EVENT0 ("btm_sec_rmt_name_request_complete"); + if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) + || ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr))) + { + btm_acl_resubmit_page(); + } + + /* If remote name request failed, p_bd_addr is null and we need to search */ + /* based on state assuming that we are doing 1 at a time */ + if (p_bd_addr) + p_dev_rec = btm_find_dev (p_bd_addr); + else + { + p_dev_rec = &btm_cb.sec_dev_rec[0]; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)) + { + p_bd_addr = p_dev_rec->bd_addr; + break; + } + } + + if (i == BTM_SEC_MAX_DEVICE_RECORDS) + p_dev_rec = NULL; + } + + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (!p_bd_name) + p_bd_name = (UINT8 *)""; + + if (p_dev_rec) + { + BTM_TRACE_EVENT5 ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d State:%d p_dev_rec: 0x%08x ", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status, p_dev_rec->sec_state, p_dev_rec); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status); + } +#endif + + if (p_dev_rec) + { + old_sec_state = p_dev_rec->sec_state; + if (status == HCI_SUCCESS) + { + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BTM_TRACE_EVENT1 ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x", p_dev_rec->sec_flags); + } + else + { + /* Notify all clients waiting for name to be resolved even if it failed so clients can continue */ + p_dev_rec->sec_bd_name[0] = 0; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* Notify all clients waiting for name to be resolved */ + for (i = 0;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i]) + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name); + } + } + else + { + dev_class[0] = 0; + dev_class[1] = 0; + dev_class[2] = 0; + + /* Notify all clients waiting for name to be resolved even if not found so clients can continue */ + for (i = 0;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i]) + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, dev_class, (UINT8 *)""); + } + + return; + } + + /* If we were delaying asking UI for a PIN because name was not resolved, ask now */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr + && (memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) ) + { + BTM_TRACE_EVENT2 ("btm_sec_rmt_name_request_complete() delayed pin now being requested flags:0x%x, (p_pin_callback=0x%p)", btm_cb.pairing_flags, btm_cb.api.p_pin_callback); + + if (((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) == 0) && + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0) && + btm_cb.api.p_pin_callback) + { + BTM_TRACE_EVENT0 ("btm_sec_rmt_name_request_complete() calling pin_callback"); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + (*btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_bd_name); + } + + /* Set the same state again to force the timer to be restarted */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + return; + } + + /* Check if we were delaying bonding because name was not resolved */ + if ( btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) + { + if (p_bd_addr && memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) + { + BTM_TRACE_EVENT2 ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x", p_dev_rec->sm4, status); + if (status != HCI_SUCCESS) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + return; + } + + /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is not reported */ + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not set. + * If it is set, there may be a race condition */ + BTM_TRACE_EVENT1 ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x", btm_cb.pairing_flags); + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) + { + p_dev_rec->sm4 |= BTM_SM4_KNOWN; + } + } + + /* BT 2.1 or carkit, bring up the connection to force the peer to request PIN. + ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if needed) + */ + if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || !btm_sec_check_prefetch_pin(p_dev_rec)) + { + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection"); + } + /* Both we and the peer are 2.1 - continue to create connection */ + else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: failed to start connection"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } + } + return; + } + else + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: wrong BDA, retry with pairing BDA"); + + BTM_ReadRemoteDeviceName (btm_cb.pairing_bda, NULL); + return; + } + } + + /* check if we were delaying link_key_callback because name was not resolved */ + if (p_dev_rec->link_key_not_sent) + { + /* If HCI connection complete has not arrived, wait for it */ + if (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) + return; + + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + { + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + + } + } + + /* If this is a bonding procedure can disconnect the link now */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete (none/ce)"); + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHENTICATE); + l2cu_start_post_bond_timer(p_dev_rec->hci_handle); + return; + } + + if (old_sec_state != BTM_SEC_STATE_GETTING_NAME) + return; + + /* If get name failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + if (p_dev_rec->sm4 & BTM_SM4_REQ_PEND) + { + BTM_TRACE_EVENT0 ("waiting for remote features!!"); + return; + } + + /* Remote Name succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status == BTM_CMD_STARTED) + return; + + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_rmt_host_support_feat_evt +** +** Description This function is called when the +** HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received +** +** Returns void +** +*******************************************************************************/ +void btm_sec_rmt_host_support_feat_evt (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR bd_addr; /* peer address */ + BD_FEATURES features; + + STREAM_TO_BDADDR (bd_addr, p); + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + BTM_TRACE_EVENT2 ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x p[0]: 0x%x", p_dev_rec->sm4, p[0]); + + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + STREAM_TO_ARRAY(features, p, BD_FEATURES_LEN); + if (HCI_SSP_HOST_SUPPORTED(features)) + { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } + BTM_TRACE_EVENT2 ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x features[0]: 0x%x", p_dev_rec->sm4, features[0]); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_req +** +** Description This function is called when LM request for the IO +** capability of the local device and +** if the OOB data is present for the device in the event +** +** Returns void +** +*******************************************************************************/ +void btm_io_capabilities_req (UINT8 *p) +{ + tBTM_SP_IO_REQ evt_data; + UINT8 err_code = 0; + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN is_orig = TRUE; + UINT8 callback_rc = BTM_SUCCESS; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + /* setup the default response according to compile options */ + /* assume that the local IO capability does not change + * loc_io_caps is initialized with the default value */ + evt_data.io_cap = btm_cb.devcb.loc_io_caps; + evt_data.oob_data = BTM_OOB_NONE; + evt_data.auth_req = BTM_DEFAULT_AUTH_REQ; + + BTM_TRACE_EVENT1 ("btm_io_capabilities_req() State: %s", btm_pair_state_descr(btm_cb.pairing_state)); + + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + p_dev_rec->sm4 |= BTM_SM4_TRUE; + + BTM_TRACE_EVENT3 ("btm_io_capabilities_req() State: %s Flags: 0x%04x p_cur_service: 0x%08x", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, p_dev_rec->p_cur_service); + + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + if (btm_cb.pairing_state == BTM_PAIR_STATE_INCOMING_SSP) + { + /* received IO capability response already-> not the originator of SSP */ + is_orig = FALSE; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + /* security is already in progress */ + else if (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) + { +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (memcmp (evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN)) + { + /* and it's not the device in bonding -> reject it */ + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + else + { + /* local device initiated dedicated bonding */ + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + } + else + { + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + } + + /* paring is not allowed */ + if (btm_cb.pairing_disabled) + err_code = HCI_ERR_PAIRING_NOT_ALLOWED; + + if (err_code != 0) + { +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + btsnd_hcic_io_cap_req_neg_reply(evt_data.bd_addr, err_code); + return; + } + + evt_data.is_orig = is_orig; + + if (is_orig) + { + /* local device initiated the pairing non-bonding -> use p_cur_service */ + if (!(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + p_dev_rec->p_cur_service && + (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_AUTHENTICATE)) + { + evt_data.auth_req = (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_MITM) ? BTM_AUTH_SP_YES : BTM_AUTH_SP_NO; + } + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS); + + callback_rc = BTM_SUCCESS; + if (p_dev_rec->sm4 & BTM_SM4_UPGRADE) + { + p_dev_rec->sm4 &= ~BTM_SM4_UPGRADE; + + /* link key upgrade: always use SPGB_YES - assuming we want to save the link key */ + evt_data.auth_req = BTM_AUTH_SPGB_YES; + } + else if (btm_cb.api.p_sp_callback) + { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_sp_callback) (BTM_SP_IO_REQ_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } + +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != evt_data.oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) + { + evt_data.auth_req = (BTM_AUTH_DD_BOND | (evt_data.auth_req & BTM_AUTH_YN_BIT)); + } + + /* if the user does not indicate "reply later" by setting the oob_data to unknown + * send the response right now. Save the current IO capability in the control block */ + btm_cb.devcb.loc_auth_req = evt_data.auth_req; + btm_cb.devcb.loc_io_caps = evt_data.io_cap; + + BTM_TRACE_EVENT4 ("btm_io_capabilities_req: State: %s IO_CAP:%d oob_data:%d auth_req:%d", + btm_pair_state_descr(btm_cb.pairing_state), evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); + + btsnd_hcic_io_cap_req_reply(evt_data.bd_addr, evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_rsp +** +** Description This function is called when the IO capability of the +** specified device is received +** +** Returns void +** +*******************************************************************************/ +void btm_io_capabilities_rsp (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SP_IO_RSP evt_data; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + STREAM_TO_UINT8 (evt_data.io_cap, p); + STREAM_TO_UINT8 (evt_data.oob_data, p); + STREAM_TO_UINT8 (evt_data.auth_req, p); + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + + /* If no security is in progress, this indicates incoming security */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_INCOMING_SSP); + + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + + /* work around for FW bug */ + btm_inq_stop_on_ssp(); + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + /* We must have a device record here. + * Use the connecting device's CoD for the connection */ +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +FALSE-POSITIVE error from Coverity test-tool. evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + + /* peer sets dedicated bonding bit and we did not initiate dedicated bonding */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_INCOMING_SSP /* peer initiated bonding */ + && (evt_data.auth_req & BTM_AUTH_DD_BOND) ) /* and dedicated bonding bit is set */ + { + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PEER_STARTED_DD; + } + + /* save the IO capability in the device record */ + p_dev_rec->rmt_io_caps = evt_data.io_cap; + p_dev_rec->rmt_auth_req = evt_data.auth_req; + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_IO_RSP_EVT, (tBTM_SP_EVT_DATA *)&evt_data); +} + +/******************************************************************************* +** +** Function btm_proc_sp_req_evt +** +** Description This function is called to process/report +** HCI_USER_CONFIRMATION_REQUEST_EVT +** or HCI_USER_PASSKEY_REQUEST_EVT +** or HCI_USER_PASSKEY_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p) +{ + tBTM_STATUS status = BTM_ERR_PROCESSING; + tBTM_SP_EVT_DATA evt_data; + UINT8 *p_bda = evt_data.cfm_req.bd_addr; + tBTM_SEC_DEV_REC *p_dev_rec; + + /* All events start with bd_addr */ + STREAM_TO_BDADDR (p_bda, p); + + BTM_TRACE_EVENT4 ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s", + (p_bda[0]<<24) + (p_bda[1]<<16) + (p_bda[2]<<8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], + event, btm_pair_state_descr(btm_cb.pairing_state)); + + if ( ((p_dev_rec = btm_find_dev (p_bda)) != NULL) + && (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) + { + memcpy (evt_data.cfm_req.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.cfm_req.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + BCM_STRNCPY_S ((char *)evt_data.cfm_req.bd_name, sizeof(evt_data.cfm_req.bd_name), (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN); + + switch (event) + { + case BTM_SP_CFM_REQ_EVT: + /* Numeric confirmation. Need user to conf the passkey */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM); + + /* The device record must be allocated in the "IO cap exchange" step */ + STREAM_TO_UINT32 (evt_data.cfm_req.num_val, p); + + evt_data.cfm_req.just_works = TRUE; + + /* process user confirm req in association with the auth_req param */ +#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_IO) + if ( (p_dev_rec->rmt_io_caps == BTM_IO_CAP_IO) + && (btm_cb.devcb.loc_io_caps == BTM_IO_CAP_IO) + && ((p_dev_rec->rmt_auth_req & BTM_AUTH_SP_YES) || (btm_cb.devcb.loc_auth_req & BTM_AUTH_SP_YES)) ) + { + /* Both devices are DisplayYesNo and one or both devices want to authenticate + -> use authenticated link key */ + evt_data.cfm_req.just_works = FALSE; + } +#endif + BTM_TRACE_DEBUG5 ("btm_proc_sp_req_evt() just_works:%d, io loc:%d, rmt:%d, auth loc:%d, rmt:%d", + evt_data.cfm_req.just_works, btm_cb.devcb.loc_io_caps, p_dev_rec->rmt_io_caps, + btm_cb.devcb.loc_auth_req, p_dev_rec->rmt_auth_req); + + evt_data.cfm_req.loc_auth_req = btm_cb.devcb.loc_auth_req; + evt_data.cfm_req.rmt_auth_req = p_dev_rec->rmt_auth_req; + evt_data.cfm_req.loc_io_caps = btm_cb.devcb.loc_io_caps; + evt_data.cfm_req.rmt_io_caps = p_dev_rec->rmt_io_caps; + break; + + case BTM_SP_KEY_NOTIF_EVT: + /* Passkey notification (other side is a keyboard) */ + STREAM_TO_UINT32 (evt_data.key_notif.passkey, p); + + BTM_TRACE_DEBUG1 ("BTM_SP_KEY_NOTIF_EVT: passkey: %u", evt_data.key_notif.passkey); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + break; + +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + case BTM_SP_KEY_REQ_EVT: + /* HCI_USER_PASSKEY_REQUEST_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_KEY_ENTRY); + break; +#endif + } + + if (btm_cb.api.p_sp_callback) + { + status = (*btm_cb.api.p_sp_callback) (event, (tBTM_SP_EVT_DATA *)&evt_data); + if (status != BTM_NOT_AUTHORIZED) + { + return; + } + /* else BTM_NOT_AUTHORIZED means when the app wants to reject the req right now */ + } + else if ( (event == BTM_SP_CFM_REQ_EVT) && (evt_data.cfm_req.just_works == TRUE) ) + { + /* automatically reply with just works if no sp_cback */ + status = BTM_SUCCESS; + } + + if (event == BTM_SP_CFM_REQ_EVT) + { + BTM_TRACE_DEBUG1 ("calling BTM_ConfirmReqReply with status: %d", status); + BTM_ConfirmReqReply (status, p_bda); + } +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + else if (event == BTM_SP_KEY_REQ_EVT) + { + BTM_PasskeyReqReply(status, p_bda, 0); + } +#endif + return; + } + + /* Something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (BTM_SP_CFM_REQ_EVT == event) + { + btsnd_hcic_user_conf_reply (p_bda, FALSE); + } + else if (BTM_SP_KEY_NOTIF_EVT == event) + { + /* do nothing -> it very unlikely to happen. + This event is most likely to be received by a HID host when it first connects to a HID device. + Usually the Host initiated the connection in this case. + On Mobile platforms, if there's a security process happening, + the host probably can not initiate another connection. + BTW (PC) is another story. */ + if (NULL != (p_dev_rec = btm_find_dev (p_bda)) ) + { + btm_sec_disconnect (p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE); + } + } +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + else + { + btsnd_hcic_user_passkey_neg_reply(p_bda); + } +#endif +} + +/******************************************************************************* +** +** Function btm_keypress_notif_evt +** +** Description This function is called when a key press notification is +** received +** +** Returns void +** +*******************************************************************************/ +void btm_keypress_notif_evt (UINT8 *p) +{ + tBTM_SP_KEYPRESS evt_data; + UINT8 *p_bda; + + /* parse & report BTM_SP_KEYPRESS_EVT */ + if (btm_cb.api.p_sp_callback) + { + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + evt_data.notif_type = *p; + + (*btm_cb.api.p_sp_callback) (BTM_SP_KEYPRESS_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } +} + +/******************************************************************************* +** +** Function btm_simple_pair_complete +** +** Description This function is called when simple pairing process is +** complete +** +** Returns void +** +*******************************************************************************/ +void btm_simple_pair_complete (UINT8 *p) +{ + tBTM_SP_COMPLT evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 status; + BOOLEAN disc = FALSE; + + status = *p++; + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + if ((p_dev_rec = btm_find_dev (evt_data.bd_addr)) == NULL) + { + BTM_TRACE_ERROR2 ("btm_simple_pair_complete() with unknown BDA: %08x%04x", + (evt_data.bd_addr[0]<<24) + (evt_data.bd_addr[1]<<16) + (evt_data.bd_addr[2]<<8) + evt_data.bd_addr[3], + (evt_data.bd_addr[4] << 8) + evt_data.bd_addr[5]); + return; + } + + BTM_TRACE_EVENT3 ("btm_simple_pair_complete() Pair State: %s Status:%d sec_state: %u", + btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state); + + evt_data.status = BTM_ERR_PROCESSING; + if (status == HCI_SUCCESS) + { + evt_data.status = BTM_SUCCESS; + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + } + else + { + if (status == HCI_ERR_PAIRING_NOT_ALLOWED) + { + /* The test spec wants the peer device to get this failure code. */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_DISCONNECT); + + /* Change the timer to 1 second */ + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } + else if (memcmp (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN) == 0) + { + /* stop the timer */ + btu_stop_timer (&btm_cb.pairing_tle); + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) + { + /* the initiating side: will receive auth complete event. disconnect ACL at that time */ + disc = TRUE; + } + } + else + disc = TRUE; + } + + /* Let the pairing state stay active, p_auth_complete_callback will report the failure */ + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + + if (disc) + { + /* simple pairing failed */ + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + } +} + +#if BTM_OOB_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_rem_oob_req +** +** Description This function is called to process/report +** HCI_REMOTE_OOB_DATA_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +void btm_rem_oob_req (UINT8 *p) +{ + UINT8 *p_bda; + tBTM_SP_RMT_OOB evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + BT_OCTET16 c; + BT_OCTET16 r; + + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + + BTM_TRACE_EVENT6 ("btm_rem_oob_req() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if ( (NULL != (p_dev_rec = btm_find_dev (p_bda))) && + btm_cb.api.p_sp_callback) + { + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + BCM_STRNCPY_S((char *)evt_data.bd_name, sizeof(evt_data.bd_name), (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN+1); + + btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP); + if ((*btm_cb.api.p_sp_callback) (BTM_SP_RMT_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data) == BTM_NOT_AUTHORIZED) + { + BTM_RemoteOobDataReply(TRUE, p_bda, c, r); + } + return; + } + + /* something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (p_bda); +} + +/******************************************************************************* +** +** Function btm_read_local_oob_complete +** +** Description This function is called when read local oob data is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_oob_complete (UINT8 *p) +{ + tBTM_SP_LOC_OOB evt_data; + UINT8 status = *p++; + + BTM_TRACE_EVENT1 ("btm_read_local_oob_complete:%d", status); + if (status == HCI_SUCCESS) + { + evt_data.status = BTM_SUCCESS; + STREAM_TO_ARRAY16(evt_data.c, p); + STREAM_TO_ARRAY16(evt_data.r, p); + } + else + evt_data.status = BTM_ERR_PROCESSING; + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_LOC_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data); +} +#endif /* BTM_OOB_INCLUDED */ + +/******************************************************************************* +** +** Function btm_sec_auth_collision +** +** Description This function is called when authentication or encryption +** needs to be retried at a later time. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_auth_collision (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (!btm_cb.collision_start_time) + btm_cb.collision_start_time = GKI_get_tick_count (); + + if ((GKI_get_tick_count () - btm_cb.collision_start_time) < btm_cb.max_collision_delay) + { + if (handle == BTM_SEC_INVALID_HANDLE) + { + if ((p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_AUTHENTICATING)) == NULL) + p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_ENCRYPTING); + } + else + p_dev_rec = btm_find_dev_by_handle (handle); + + if (p_dev_rec != NULL) + { + BTM_TRACE_DEBUG1 ("btm_sec_auth_collision: state %d (retrying in a moment...)", p_dev_rec->sec_state); + /* We will restart authentication after timeout */ + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING || p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + p_dev_rec->sec_state = 0; + + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_collision_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_auth_complete +** +** Description This function is when authentication of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_auth_complete (UINT16 handle, UINT8 status) +{ + UINT8 old_sm4; + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BOOLEAN are_bonding = FALSE; + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT6 ("Security Manager: auth_complete PairState: %s handle:%u status:%d dev->sec_state: %u Bda:%08x, RName:%s", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status, + p_dev_rec->sec_state, + (p_dev_rec->bd_addr[2]<<24)+(p_dev_rec->bd_addr[3]<<16)+(p_dev_rec->bd_addr[4]<<8)+p_dev_rec->bd_addr[5], + p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: auth_complete PairState: %s handle:%u status:%d", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status); + } +#endif + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) + { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + btm_restore_mode(); + + /* Check if connection was made just to do bonding. If we authenticate + the connection that is up, this is the last event received. + */ + if (p_dev_rec + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && !(btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + } + + if (!p_dev_rec) + return; + + /* keep the old sm4 flag and clear the retry bit in control block */ + old_sm4 = p_dev_rec->sm4; + p_dev_rec->sm4 &= ~BTM_SM4_RETRY; + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) ) + are_bonding = TRUE; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) + { + if ( (btm_cb.api.p_auth_complete_callback && status != HCI_SUCCESS) + && (old_state != BTM_PAIR_STATE_IDLE) ) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + return; + } + + /* There can be a race condition, when we are starting authentication and + ** the peer device is doing encryption. + ** If first we receive encryption change up, then initiated authentication + ** can not be performed. According to the spec we can not do authentication + ** on the encrypted link, so device is correct. + */ + if ((status == HCI_ERR_COMMAND_DISALLOWED) + && ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) == + (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) + { + status = HCI_SUCCESS; + } + /* Currently we do not notify user if it is a keyboard which connects */ + /* User probably Disabled the keyboard while it was asleap. Let her try */ + if (btm_cb.api.p_auth_complete_callback) + { + /* report the suthentication status */ + if (old_state != BTM_PAIR_STATE_IDLE) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* If this is a bonding procedure can disconnect the link now */ + if (are_bonding) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + if (status != HCI_SUCCESS) + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_PEER_USER); + else + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + + return; + } + + /* If authentication failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + if ((old_sm4 & BTM_SM4_RETRY) == 0) + { + /* allow retry only once */ + if (status == HCI_ERR_LMP_ERR_TRANS_COLLISION) + { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + BTM_TRACE_DEBUG2 ("Collision retry sm4:x%x sec_flags:0x%x", p_dev_rec->sm4, p_dev_rec->sec_flags); + } + /* this retry for missing key is for Lisbon or later only. + * Legacy device do not need this. the controller will drive the retry automatically */ + else if (HCI_ERR_KEY_MISSING == status && BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + BTM_TRACE_DEBUG2 ("Retry for missing key sm4:x%x sec_flags:0x%x", p_dev_rec->sm4, p_dev_rec->sec_flags); + + /* With BRCM controller, we do not need to delete the stored link key in controller. + If the stack may sit on top of other controller, we may need this + BTM_DeleteStoredLinkKey (bd_addr, NULL); */ + } + + if (p_dev_rec->sm4 & BTM_SM4_RETRY) + { + btm_sec_execute_procedure (p_dev_rec); + return; + } + } + + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + } + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + /* Authentication succeeded, execute the next security procedure, if any */ + status = btm_sec_execute_procedure (p_dev_rec); + + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_mkey_comp_event +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_mkey_comp_event (UINT16 handle, UINT8 status, UINT8 key_flg) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 bd_addr[BD_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ; + + BTM_TRACE_EVENT2 ("Security Manager: mkey comp status:%d State:%d", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0); + + /* If encryption setup failed, notify the waiting layer */ + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + if (btm_cb.mkey_cback) + { + if (!p_dev_rec) + (btm_cb.mkey_cback)(bd_addr, status, key_flg ); + else + (btm_cb.mkey_cback)(p_dev_rec->bd_addr, status, key_flg ); + } +} + +/******************************************************************************* +** +** Function btm_sec_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + BTM_TRACE_EVENT3 ("Security Manager: encrypt_change status:%d State:%d, encr_enable = %d", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0, encr_enable); + BTM_TRACE_DEBUG1 ("before update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags ); + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) + { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + if (!p_dev_rec) + return; + + if ((status == HCI_SUCCESS) && encr_enable) + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + + /* It is possible that we decrypted the link to perform role switch */ + /* mark link not to be encrypted, so that when we execute security next time it will kick in again */ + if ((status == HCI_SUCCESS) && !encr_enable) + p_dev_rec->sec_flags &= ~BTM_SEC_ENCRYPTED; + + BTM_TRACE_DEBUG1 ("after update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags ); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + btm_ble_link_encrypted(p_dev_rec->bd_addr, encr_enable); + return; + } + else + /* BR/EDR connection, update the encryption key size to be 16 as always */ + p_dev_rec->enc_key_size = 16; +#endif + + /* If this encryption was started by peer do not need to do anything */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_ENCRYPTING) + { + if (BTM_SEC_STATE_DELAY_FOR_ENC == p_dev_rec->sec_state) + { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + } + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* If encryption setup failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + /* Encryption setup succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_create_conn +** +** Description This function records current role and forwards request to +** HCI +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_sec_create_conn (BD_ADDR bda, UINT16 packet_types, + UINT8 page_scan_rep_mode, UINT8 page_scan_mode, + UINT16 clock_offset, UINT8 allow_switch) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda); + + memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + btm_cb.acl_disc_reason = 0xff ; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->role_master = TRUE; + + /* If any SCO link up, do not allow a switch */ + if (BTM_GetNumScoLinks() != 0) + allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH; + + return(btsnd_hcic_create_conn (bda, packet_types, page_scan_rep_mode, + page_scan_mode, clock_offset, allow_switch)); +} + +/******************************************************************************* +** +** Function btm_sec_connect_after_reject_timeout +** +** Description Connection for bonding could not start because of the collision +** Initiate outgoing connection +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_connect_after_reject_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_cb.p_collided_dev_rec; + + BTM_TRACE_EVENT0 ("btm_sec_connect_after_reject_timeout()"); + btm_cb.sec_collision_tle.param = 0; + btm_cb.p_collided_dev_rec = 0; + + if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connect_after_reject_timeout: failed to start connection"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } +} + +/******************************************************************************* +** +** Function btm_sec_connected +** +** Description This function is when a connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + UINT8 res; + BOOLEAN is_pairing_device = FALSE; + tACL_CONN *p_acl_cb; + + btm_acl_resubmit_page(); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT6 ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x RName:%s", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5], + p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT5 ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x ", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5]); + } +#endif + + if (!p_dev_rec) + { + /* There is no device record for new connection. Allocate one */ + if (status == HCI_SUCCESS) + { + p_dev_rec = btm_sec_alloc_dev (bda); + } + else + { + /* can not find the device record and the status is error, + * just ignore it */ + return; + } + } + else /* Update the timestamp for this device */ + { + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) + { + /* tell L2CAP it's a bonding connection. */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) + { + /* if incoming connection failed while pairing, then try to connect and continue */ + /* Motorola S9 disconnects without asking pin code */ + if ((status != HCI_SUCCESS)&&(btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ)) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connected: incoming connection failed without asking PIN"); + + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(p_dev_rec->bd_addr, NULL); + } +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + return; + } + else + { + l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, TRUE); + } + } + /* always clear the pending flag */ + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + } + } + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0) ) + { + /* if we rejected incoming connection from bonding device */ + if ((status == HCI_ERR_HOST_REJECT_DEVICE) + &&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) + { + BTM_TRACE_WARNING2 ("Security Manager: btm_sec_connected: HCI_Conn_Comp Flags:0x%04x, sm4: 0x%x", + btm_cb.pairing_flags, p_dev_rec->sm4); + + btm_cb.pairing_flags &= ~BTM_PAIR_FLAGS_REJECTED_CONNECT; + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* Try again: RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bda, NULL); + return; + } + + /* if we already have pin code */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) + { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } + + return; + } + /* wait for incoming connection without resetting pairing state */ + else if (status == HCI_ERR_CONNECTION_EXISTS) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connected: Wait for incoming connection"); + return; + } + + is_pairing_device = TRUE; + } + + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* if connection fails during pin request, notify application */ + if (status != HCI_SUCCESS) + { + /* If connection failed because of during pairing, need to tell user */ + if (is_pairing_device) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); + BTM_TRACE_DEBUG1 ("security_required:%x ", p_dev_rec->security_required ); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + } + else if ((status == HCI_ERR_AUTH_FAILURE) || + (status == HCI_ERR_KEY_MISSING) || + (status == HCI_ERR_HOST_REJECT_SECURITY) || + (status == HCI_ERR_PAIRING_NOT_ALLOWED) || + (status == HCI_ERR_UNIT_KEY_USED) || + (status == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (status == HCI_ERR_REPEATED_ATTEMPTS)) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + } + + if (status == HCI_ERR_CONNECTION_TOUT || status == HCI_ERR_LMP_RESPONSE_TIMEOUT || + status == HCI_ERR_UNSPECIFIED || status == HCI_ERR_PAGE_TIMEOUT) + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_DEVICE_TIMEOUT); + else + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + + return; + } + + /* If initiated dedicated bonding, return the link key now, and initiate disconnect */ + /* If dedicated bonding, and we now have a link key, we are all done */ + if ( is_pairing_device + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) ) + { + if (p_dev_rec->link_key_not_sent) + { + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + } + + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + /* remember flag before it is initialized */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + res = TRUE; + else + res = FALSE; + + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if ( res ) + { + /* Let l2cap start bond timer */ + l2cu_update_lcb_4_bonding (p_dev_rec->bd_addr, TRUE); + } + + return; + } + + p_dev_rec->hci_handle = handle; + + /* role may not be correct here, it will be updated by l2cap, but we need to */ + /* notify btm_acl that link is up, so starting of rmt name request will not */ + /* set paging flag up */ + p_acl_cb = btm_bda_to_acl(bda); + if (p_acl_cb) + { + /* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT event */ +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) + /* For now there are a some devices that do not like sending */ + /* commands events and data at the same time. */ + /* Set the packet types to the default allowed by the device */ + btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); + + if (btm_cb.btm_def_link_policy) + BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); +#endif + + BTM_SetLinkSuperTout (p_acl_cb->remote_addr, btm_cb.btm_def_link_super_tout); + } + btm_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, FALSE); + + /* Initialize security flags. We need to do that because some */ + /* authorization complete could have come after the connection is dropped */ + /* and that would set wrong flag that link has been authorized already */ + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | + BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + + if (enc_mode != HCI_ENCRYPT_MODE_DISABLED) + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + p_dev_rec->link_key_changed = FALSE; + + /* After connection is established we perform security if we do not know */ + /* the name, or if we are originator because some procedure can have */ + /* been scheduled while connection was down */ + BTM_TRACE_DEBUG1 ("is_originator:%d ", p_dev_rec->is_originator); + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) || p_dev_rec->is_originator) + { + if ((res = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } + return; +} + +/******************************************************************************* +** +** Function btm_sec_role_changed +** +** Description This function is colled when controller reports role +** changed, or failed command status for Role Change request +** +** Returns void +** +*******************************************************************************/ +void btm_sec_role_changed (void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = (tBTM_SEC_DEV_REC *)p_ref_data; + UINT8 res; + + BTM_TRACE_EVENT0 ("Security Manager: role changed"); + + /* If this role switch was started by peer do not need to do anything */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_SWITCHING_ROLE) + return; + + /* If serurity required was to FORCE switch and it failed, notify the waiting layer */ + if (((p_dev_rec->security_required & BTM_SEC_FORCE_MASTER) && !p_dev_rec->role_master) + || ((p_dev_rec->security_required & BTM_SEC_FORCE_SLAVE) && p_dev_rec->role_master)) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_ROLE_SWITCHED; + + p_dev_rec->security_required &= ~(BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | + BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + if ((res = (UINT8)btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } +} + +/******************************************************************************* +** +** Function btm_sec_disconnect +** +** Description This function is called to disconnect HCI link +** +** Returns btm status +** +*******************************************************************************/ +tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + /* In some weird race condition we may not have a record */ + if (!p_dev_rec) + { + btsnd_hcic_disconnect (handle, reason); + return(BTM_SUCCESS); + } + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) + { + /* we are currently doing bonding. Link will be disconnected when done */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + return(BTM_BUSY); + } + + return(btm_sec_send_hci_disconnect(p_dev_rec, reason)); +} + +/******************************************************************************* +** +** Function btm_sec_disconnected +** +** Description This function is when a connection to the peer device is +** dropped +** +** Returns void +** +*******************************************************************************/ +void btm_sec_disconnected (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 old_pairing_flags = btm_cb.pairing_flags; + int result = HCI_ERR_AUTH_FAILURE; + + /* If page was delayed for disc complete, can do it now */ + btm_cb.discing = FALSE; + + btm_acl_resubmit_page(); + + if (!p_dev_rec) + return; + +#if BTM_DISC_DURING_RS == TRUE + BTM_TRACE_ERROR0("btm_sec_disconnected - Clearing Pending flag"); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + /* clear unused flags */ + p_dev_rec->sm4 &= BTM_SM4_TRUE; + + BTM_TRACE_EVENT6("btm_sec_disconnected() sec_req:x%x State: %s reason:%d bda:%04x%08x RName:%s", + p_dev_rec->security_required, btm_pair_state_descr(btm_cb.pairing_state), reason, (p_dev_rec->bd_addr[0]<<8)+p_dev_rec->bd_addr[1], + (p_dev_rec->bd_addr[2]<<24)+(p_dev_rec->bd_addr[3]<<16)+(p_dev_rec->bd_addr[4]<<8)+p_dev_rec->bd_addr[5], p_dev_rec->sec_bd_name); + + BTM_TRACE_EVENT1("before Update sec_flags=0x%x", p_dev_rec->sec_flags); + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0)) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + if (btm_cb.api.p_auth_complete_callback) + { + /* If the disconnection reason is REPEATED_ATTEMPTS, + send this error message to complete callback function + to display the error message of Repeated attempts. + All others, send HCI_ERR_AUTH_FAILURE. */ + if (reason == HCI_ERR_REPEATED_ATTEMPTS) + { + result = HCI_ERR_REPEATED_ATTEMPTS; + } + else if (old_pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + { + result = HCI_ERR_HOST_REJECT_SECURITY; + } + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, result); + } + } + + p_dev_rec->hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + p_dev_rec->enc_key_size = 0; + btm_ble_resume_bg_conn(NULL, TRUE); + /* see sec_flags processing in btm_acl_removed */ +#endif + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + + p_dev_rec->security_required = BTM_SEC_NONE; + p_dev_rec->p_callback = NULL; /* when the peer device time out the authentication before we do, this call back must be reset here */ + BTM_TRACE_EVENT1("after Update sec_flags=0x%x", p_dev_rec->sec_flags); +} + +/******************************************************************************* +** +** Function btm_sec_link_key_notification +** +** Description This function is called when a new connection link key is +** generated +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + BOOLEAN we_are_bonding = FALSE; + + BTM_TRACE_EVENT3 ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d", + (p_bda[0]<<8)+p_bda[1], (p_bda[2]<<24)+(p_bda[3]<<16)+(p_bda[4]<<8)+p_bda[5], + key_type); + + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* Override the key type if version is pre-1.1 */ + if (btm_cb.devcb.local_version.hci_version < HCI_VERSION_1_1) + p_dev_rec->link_key_type = BTM_LKEY_TYPE_IGNORE; + if (key_type != BTM_LKEY_TYPE_CHANGED_COMB) + p_dev_rec->link_key_type = key_type; + + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + + memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN); + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) + { + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + we_are_bonding = TRUE; + else + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + /* If name is not known at this point delay calling callback until the name is */ + /* resolved. Unless it is a HID Device and we really need to send all link keys. */ + if ((!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) != BTM_COD_MAJOR_PERIPHERAL)) ) + { + BTM_TRACE_EVENT3 ("btm_sec_link_key_notification() Delayed BDA: %08x%04x Type:%d", + (p_bda[0]<<24) + (p_bda[1]<<16) + (p_bda[2]<<8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], key_type); + + p_dev_rec->link_key_not_sent = TRUE; + + /* If it is for bonding nothing else will follow, so we need to start name resolution */ + if (we_are_bonding) + { + if (!(btsnd_hcic_rmt_name_req (p_bda, HCI_PAGE_SCAN_REP_MODE_R1, HCI_MANDATARY_PAGE_SCAN_MODE, 0))) + btm_inq_rmt_name_failed(); + } + + BTM_TRACE_EVENT3 ("rmt_io_caps:%d, sec_flags:x%x, dev_class[1]:x%02x", p_dev_rec->rmt_io_caps, p_dev_rec->sec_flags, p_dev_rec->dev_class[1]) + return; + } + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + { + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + } + + /* We will save link key only if the user authorized it - BTE report link key in all cases */ +#ifdef BRCM_NONE_BTE + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED) +#endif + { + if (btm_cb.api.p_link_key_callback) + { + (*btm_cb.api.p_link_key_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, + p_link_key, p_dev_rec->link_key_type); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_request +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + + BTM_TRACE_EVENT6 ("btm_sec_link_key_request() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + { + btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key); + return; + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (p_bda); + + /* Only ask the host for a key if this guy is not already bonding */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + || (memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) ) + { + if (btm_cb.api.p_link_key_req_callback) + { + if ((*btm_cb.api.p_link_key_req_callback)(p_bda, p_dev_rec->link_key) == BTM_SUCCESS) + { + btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key); + return; + } + } + } + + /* The link key is not in the database and it is not known to the manager */ + btsnd_hcic_link_key_neg_reply (p_bda); +} + +/******************************************************************************* +** +** Function btm_sec_pairing_timeout +** +** Description This function is called when host does not provide PIN +** within requested time +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec; +#if BTM_OOB_INCLUDED == TRUE +#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_NONE) + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_NO; +#else + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_YES; +#endif +#endif + UINT8 name[2]; + + p_cb->pairing_tle.param = 0; +/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[UNUSED_VALUE] pointer p_dev_rec is actually used several times... This is a Coverity false-positive, i.e. a fake issue. +*/ + p_dev_rec = btm_find_dev (p_cb->pairing_bda); + + BTM_TRACE_EVENT2 ("btm_sec_pairing_timeout() State: %s Flags: %u", + btm_pair_state_descr(p_cb->pairing_state), p_cb->pairing_flags); + + switch (p_cb->pairing_state) + { + case BTM_PAIR_STATE_WAIT_PIN_REQ: + btm_sec_bond_cancel_complete(); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PRE_FETCH_PIN) == 0) + btsnd_hcic_pin_code_neg_reply (p_cb->pairing_bda); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + /* We need to notify the UI that no longer need the PIN */ + if (btm_cb.api.p_auth_complete_callback) + { + if (p_dev_rec == NULL) + { + name[0] = 0; + (*btm_cb.api.p_auth_complete_callback) (p_cb->pairing_bda, + NULL, + name, HCI_ERR_CONNECTION_TOUT); + } + else + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_CONNECTION_TOUT); + } + break; + + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: + btsnd_hcic_user_conf_reply (p_cb->pairing_bda, FALSE); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; + +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + case BTM_PAIR_STATE_KEY_ENTRY: + btsnd_hcic_user_passkey_neg_reply(p_cb->pairing_bda); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; +#endif /* !BTM_IO_CAP_NONE */ + +#if BTM_OOB_INCLUDED == TRUE + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + auth_req |= BTM_AUTH_DD_BOND; + + btsnd_hcic_io_cap_req_reply (p_cb->pairing_bda, btm_cb.devcb.loc_io_caps, + BTM_OOB_NONE, auth_req); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: + btsnd_hcic_rem_oob_neg_reply (p_cb->pairing_bda); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; +#endif /* BTM_OOB_INCLUDED */ + + case BTM_PAIR_STATE_WAIT_DISCONNECT: + /* simple pairing failed. Started a 1-sec timer at simple pairing complete. + * now it's time to tear down the ACL link*/ + if (p_dev_rec == NULL) + { + BTM_TRACE_ERROR2 ("btm_sec_pairing_timeout() BTM_PAIR_STATE_WAIT_DISCONNECT unknown BDA: %08x%04x", + (p_cb->pairing_bda[0]<<24) + (p_cb->pairing_bda[1]<<16) + (p_cb->pairing_bda[2]<<8) + p_cb->pairing_bda[3], + (p_cb->pairing_bda[4] << 8) + p_cb->pairing_bda[5]); + break; + } + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: + /* We need to notify the UI that timeout has happened while waiting for authentication*/ + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + if (btm_cb.api.p_auth_complete_callback) + { + if (p_dev_rec == NULL) + { + name[0] = 0; + (*btm_cb.api.p_auth_complete_callback) (p_cb->pairing_bda, + NULL, + name, HCI_ERR_CONNECTION_TOUT); + } + else + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_CONNECTION_TOUT); + } + break; + + default: + BTM_TRACE_WARNING1 ("btm_sec_pairing_timeout() not processed state: %s", btm_pair_state_descr(btm_cb.pairing_state)); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + } +} + +/******************************************************************************* +** +** Function btm_sec_pin_code_request +** +** Description This function is called when controller requests PIN code +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_pin_code_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_CB *p_cb = &btm_cb; + + BTM_TRACE_EVENT3 ("btm_sec_pin_code_request() State: %s, BDA:%04x%08x", + btm_pair_state_descr(btm_cb.pairing_state), + (p_bda[0]<<8)+p_bda[1], (p_bda[2]<<24)+(p_bda[3]<<16)+(p_bda[4]<<8)+p_bda[5] ); + + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + if ( (memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) && + (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) ) + { + /* fake this out - porshe carkit issue - */ +// btm_cb.pairing_state = BTM_PAIR_STATE_IDLE; + if(! btm_cb.pin_code_len_saved) + { + btsnd_hcic_pin_code_neg_reply (p_bda); + return; + } + else + { + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + return; + } + } + else if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_PIN_REQ) + || memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) + { + BTM_TRACE_WARNING1 ("btm_sec_pin_code_request() rejected - state: %s", + btm_pair_state_descr(btm_cb.pairing_state)); + +#ifdef PORCHE_PAIRING_CONFLICT + /* reply pin code again due to counter in_rand when local initiates pairing */ + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request from remote dev. for local initiated pairing"); + if(! btm_cb.pin_code_len_saved) + { + btsnd_hcic_pin_code_neg_reply (p_bda); + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + } +#else + btsnd_hcic_pin_code_neg_reply (p_bda); +#endif + return; + } + } + + p_dev_rec = btm_find_or_alloc_dev (p_bda); + /* received PIN code request. must be non-sm4 */ + p_dev_rec->sm4 = BTM_SM4_KNOWN; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + memcpy (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_PEER_STARTED_DD; + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + } + + if (!p_cb->pairing_disabled && (p_cb->cfg.pin_type == HCI_PIN_TYPE_FIXED)) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request fixed pin replying"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, p_cb->cfg.pin_code_len, p_cb->cfg.pin_code); + return; + } + + /* Use the connecting device's CoD for the connection */ + if ( (!memcmp (p_bda, p_cb->connecting_bda, BD_ADDR_LEN)) + && (p_cb->connecting_dc[0] || p_cb->connecting_dc[1] || p_cb->connecting_dc[2]) ) + memcpy (p_dev_rec->dev_class, p_cb->connecting_dc, DEV_CLASS_LEN); + + /* We could have started connection after asking user for the PIN code */ + if (btm_cb.pin_code_len != 0) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request bonding sending reply"); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len, p_cb->pin_code); + +#ifdef PORCHE_PAIRING_CONFLICT + btm_cb.pin_code_len_saved = btm_cb.pin_code_len; +#endif + + /* Mark that we forwarded received from the user PIN code */ + btm_cb.pin_code_len = 0; + + /* We can change mode back right away, that other connection being established */ + /* is not forced to be secure - found a FW issue, so we can not do this + btm_restore_mode(); */ + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + } + + /* If pairing disabled OR (no PIN callback and not bonding) */ + /* OR we could not allocate entry in the database reject pairing request */ + else if (p_cb->pairing_disabled + || (p_cb->api.p_pin_callback == NULL) + + /* OR Microsoft keyboard can for some reason try to establish connection */ + /* the only thing we can do here is to shut it up. Normally we will be originator */ + /* for keyboard bonding */ + || (!p_dev_rec->is_originator + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)) ) + { + BTM_TRACE_WARNING3("btm_sec_pin_code_request(): Pairing disabled:%d; PIN callback:%x, Dev Rec:%x!", + p_cb->pairing_disabled, p_cb->api.p_pin_callback, p_dev_rec); + + btsnd_hcic_pin_code_neg_reply (p_bda); + } + /* Notify upper layer of PIN request and start expiration timer */ + else + { + btm_cb.pin_code_len_saved = 0; + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + /* Pin code request can not come at the same time as connection request */ + memcpy (p_cb->connecting_bda, p_bda, BD_ADDR_LEN); + memcpy (p_cb->connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + /* Check if the name is known */ + /* Even if name is not known we might not be able to get one */ + /* this is the case when we are already getting something from the */ + /* device, so HCI level is flow controlled */ + /* Also cannot send remote name request while paging, i.e. connection is not completed */ + if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request going for callback"); + + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + if (p_cb->api.p_pin_callback) + (*p_cb->api.p_pin_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request going for remote name"); + + /* We received PIN code request for the device with unknown name */ + /* it is not user friendly just to ask for the PIN without name */ + /* try to get name at first */ + if (!btsnd_hcic_rmt_name_req (p_dev_rec->bd_addr, + HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0)) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + p_dev_rec->sec_bd_name[0] = 'f'; + p_dev_rec->sec_bd_name[1] = '0'; + BTM_TRACE_ERROR0 ("can not send rmt_name_req?? fake a name and call callback"); + + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + if (p_cb->api.p_pin_callback) + (*p_cb->api.p_pin_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + } + } + + return; +} + +/******************************************************************************* +** +** Function btm_sec_update_clock_offset +** +** Description This function is called to update clock offset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_INQ_INFO *p_inq_info; + + if ((p_dev_rec = btm_find_dev_by_handle (handle)) == NULL) + return; + + p_dev_rec->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + if ((p_inq_info = BTM_InqDbRead(p_dev_rec->bd_addr)) == NULL) + return; + + p_inq_info->results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; +} + + +/****************************************************************** +** S T A T I C F U N C T I O N S +*******************************************************************/ + +/******************************************************************************* +** +** Function btm_sec_execute_procedure +** +** Description This function is called to start required security +** procedure. There is a case when multiplexing protocol +** calls this function on the originating side, connection to +** the peer will not be established. This function in this +** case performs only authorization. +** +** Returns BTM_SUCCESS - permission is granted +** BTM_CMD_STARTED - in process +** BTM_NO_RESOURCES - permission declined +** +*******************************************************************************/ +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BTM_TRACE_EVENT3 ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d", + p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state); + + /* There is a chance that we are getting name. Wait until done. */ + if (p_dev_rec->sec_state != 0) + return(BTM_CMD_STARTED); + + /* If any security is required, get the name first */ + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { + BTM_TRACE_EVENT0 ("Security Manager: Start get name"); + if (!btm_sec_start_get_name (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not authenticated and authentication is required */ + /* start authentication and return PENDING to the caller */ + if ((!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHENTICATE))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) + return(BTM_FAILED_ON_SECURITY); +#endif + + BTM_TRACE_EVENT0 ("Security Manager: Start authentication"); + + if (!btm_sec_start_authentication (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not encrypted and encryption is required */ + /* start encryption and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) + return(BTM_FAILED_ON_SECURITY); +#endif + + BTM_TRACE_EVENT0 ("Security Manager: Start encryption"); + + if (!btm_sec_start_encryption (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not authorized and authorization is required */ + /* start authorization and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHORIZE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHORIZE)))) + { + BTM_TRACE_EVENT2 ("service id:%d, is trusted:%d", + p_dev_rec->p_cur_service->service_id, + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id))); + if ((btm_sec_are_all_trusted(p_dev_rec->trusted_mask) == FALSE) && + (p_dev_rec->p_cur_service->service_id < BTM_SEC_MAX_SERVICES) && + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id) == FALSE)) + { + BTM_TRACE_EVENT0 ("Security Manager: Start authorization"); + return(btm_sec_start_authorization (p_dev_rec)); + } + } + + /* All required security procedures already established */ + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE | + BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE | + BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | + BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + + BTM_TRACE_EVENT2 ("Security Manager: trusted:0x%04x%04x", p_dev_rec->trusted_mask[1], p_dev_rec->trusted_mask[0]); + BTM_TRACE_EVENT0 ("Security Manager: access granted"); + + return(BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_sec_start_get_name +** +** Description This function is called to start get name procedure +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 tempstate = p_dev_rec->sec_state; + + p_dev_rec->sec_state = BTM_SEC_STATE_GETTING_NAME; + + /* Device should be connected, no need to provide correct page params */ + /* 0 and NULL are as timeout and callback params because they are not used in security get name case */ + if ((btm_initiate_rem_name (p_dev_rec->bd_addr, NULL, BTM_RMT_NAME_SEC, + 0, NULL)) != BTM_CMD_STARTED) + { + p_dev_rec->sec_state = tempstate; + return(FALSE); + } + + return(TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_start_authentication +** +** Description This function is called to start authentication +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec) +{ + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + + return(btsnd_hcic_auth_request (p_dev_rec->hci_handle)); +} + +/******************************************************************************* +** +** Function btm_sec_start_encryption +** +** Description This function is called to start encryption +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (!btsnd_hcic_set_conn_encrypt (p_dev_rec->hci_handle, TRUE)) + return(FALSE); + + p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + return(TRUE); +} + + +/******************************************************************************* +** +** Function btm_sec_start_authorization +** +** Description This function is called to start authorization +** +** Returns TRUE if started +** +*******************************************************************************/ +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 result; + UINT8 *p_service_name = NULL; + UINT8 service_id; + + if ((p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + || (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) + { + if (!btm_cb.api.p_authorize_callback) + return(BTM_MODE_UNSUPPORTED); + + if (p_dev_rec->p_cur_service) + { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_dev_rec->is_originator) + p_service_name = p_dev_rec->p_cur_service->orig_service_name; + else + p_service_name = p_dev_rec->p_cur_service->term_service_name; +#endif + service_id = p_dev_rec->p_cur_service->service_id; + } + else + service_id = 0; + + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHORIZING; + result = (*btm_cb.api.p_authorize_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, + p_service_name, + service_id, + p_dev_rec->is_originator); + if (result == BTM_SUCCESS) + { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + return(result); + } + btm_sec_start_get_name (p_dev_rec); + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_sec_are_all_trusted +** +** Description This function is called check if all services are trusted +** +** Returns TRUE if all are trusted, otherwise FALSE +** +*******************************************************************************/ +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]) +{ + int trusted_inx; + for (trusted_inx = 0; trusted_inx < BTM_SEC_SERVICE_ARRAY_SIZE; trusted_inx++) + { + if (p_mask[trusted_inx] != BTM_SEC_TRUST_ALL) + return(FALSE); + } + + return(TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_find_first_serv +** +** Description Look for the first record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + BOOLEAN is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + + if ( conn_type & CONNECTION_TYPE_ORIG_MASK ) + is_originator = TRUE; + else + is_originator = FALSE; +#else + is_originator = conn_type; +#endif + + if (is_originator && btm_cb.p_out_serv && btm_cb.p_out_serv->psm == psm) + { + /* If this is outgoing connection and the PSM matches p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, just find the first record with the specified PSM */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ( (p_serv_rec->security_flags & BTM_SEC_IN_USE) && (p_serv_rec->psm == psm) ) + return(p_serv_rec); + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_find_next_serv +** +** Description Look for the next record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == p_cur->psm) ) + { + if (p_cur != p_serv_rec) + { + return(p_serv_rec); + } + } + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_find_mx_serv +** +** Description Look for the record in the service database with specified +** PSM and multiplexor channel information +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, UINT32 mx_chan_id) +{ + tBTM_SEC_SERV_REC *p_out_serv = btm_cb.p_out_serv; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + BTM_TRACE_DEBUG0 ("btm_sec_find_mx_serv"); + if (is_originator && p_out_serv && p_out_serv->psm == psm + && p_out_serv->mx_proto_id == mx_proto_id + && p_out_serv->orig_mx_chan_id == mx_chan_id) + { + /* If this is outgoing connection and the parameters match p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, the old way */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == psm) + && (p_serv_rec->mx_proto_id == mx_proto_id) + && (( is_originator && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) + || (!is_originator && (p_serv_rec->term_mx_chan_id == mx_chan_id)))) + { + return(p_serv_rec); + } + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_collision_timeout +** +** Description Encryption could not start because of the collision +** try to do it again +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_STATUS status; + + BTM_TRACE_EVENT0 ("btm_sec_collision_timeout()"); + btm_cb.sec_collision_tle.param = 0; + + status = btm_sec_execute_procedure (btm_cb.p_collided_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status != BTM_CMD_STARTED) + { + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (btm_cb.p_collided_dev_rec, status); + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_request +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (btm_cb.api.p_link_key_callback) + (*btm_cb.api.p_link_key_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, p_dev_rec->link_key, + p_dev_rec->link_key_type); +} + +/******************************************************************************* +** +** Function BTM_ReadTrustedMask +** +** Description Get trusted mask for the peer device +** +** Parameters: bd_addr - Address of the device +** +** Returns NULL, if the device record is not found. +** otherwise, the trusted mask +** +*******************************************************************************/ +UINT32 * BTM_ReadTrustedMask (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + return(p_dev_rec->trusted_mask); + } + else + { + return NULL; + } +} + +/******************************************************************************* +** +** Function btm_restore_mode +** +** Description This function returns the security mode to previous setting +** if it was changed during bonding. +** +** +** Parameters: void +** +*******************************************************************************/ +static void btm_restore_mode(void) +{ + if (btm_cb.security_mode_changed) + { + btm_cb.security_mode_changed = FALSE; + BTM_TRACE_DEBUG1("btm_restore_mode: Authen Enable -> %d", (btm_cb.security_mode == BTM_SEC_MODE_LINK)); + btsnd_hcic_write_auth_enable ((UINT8)(btm_cb.security_mode == BTM_SEC_MODE_LINK)); + } + + if (btm_cb.pin_type_changed) + { + btm_cb.pin_type_changed = FALSE; + btsnd_hcic_write_pin_type (btm_cb.cfg.pin_type); + } +} + + +/******************************************************************************* +** +** Function btm_sec_find_dev_by_sec_state +** +** Description Look for the record in the device database for the device +** which is being authenticated or encrypted +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == state)) + return(p_dev_rec); + } + return(NULL); +} + +/******************************************************************************* +** +** Function BTM_snd_conn_encrypt +** +** Description This function is called to start/stop encryption +** Used by JSR-82 +** +** Returns TRUE if request started +** +*******************************************************************************/ +BOOLEAN BTM_snd_conn_encrypt (UINT16 handle, BOOLEAN enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + BTM_TRACE_EVENT2 ("BTM_snd_conn_encrypt Security Manager: encrypt_change p_dev_rec : 0x%x, enable = %s", p_dev_rec, (enable == TRUE) ? "TRUE" : "FALSE"); + + if (!p_dev_rec) + { + BTM_TRACE_EVENT1 ("BTM_snd_conn_encrypt Error no p_dev_rec : 0x%x\n", p_dev_rec); + return(FALSE); + } + + if ( p_dev_rec->sec_state == BTM_SEC_STATE_IDLE) + { + if (!btsnd_hcic_set_conn_encrypt (handle, enable)) + return(FALSE); + + p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + + return(TRUE); + } + else + return(FALSE); +} + +/******************************************************************************* +** +** Function btm_sec_change_pairing_state +** +** Description This function is called to change pairing state +** +*******************************************************************************/ +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state) +{ + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; + + BTM_TRACE_EVENT1 ("btm_sec_change_pairing_state Old: %s", btm_pair_state_descr(btm_cb.pairing_state)); + BTM_TRACE_EVENT2 ("btm_sec_change_pairing_state New: %s pairing_flags:0x%x",btm_pair_state_descr(new_state), btm_cb.pairing_flags); + + btm_cb.pairing_state = new_state; + + if (new_state == BTM_PAIR_STATE_IDLE) + { + btu_stop_timer (&btm_cb.pairing_tle); + + btm_cb.pairing_flags = 0; + btm_cb.pin_code_len = 0; + + /* Make sure the the lcb shows we are not bonding */ + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, FALSE); + + btm_restore_mode(); + btm_sec_check_pending_reqs(); + btm_inq_clear_ssp(); + + memset (btm_cb.pairing_bda, 0xFF, BD_ADDR_LEN); + } + else + { + /* If transitionng out of idle, mark the lcb as bonding */ + if (old_state == BTM_PAIR_STATE_IDLE) + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, TRUE); + + btm_cb.pairing_tle.param = (TIMER_PARAM_TYPE)btm_sec_pairing_timeout; + + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BTM_SEC_TIMEOUT_VALUE); + } +} + + +/******************************************************************************* +** +** Function btm_pair_state_descr +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (BT_USE_TRACES == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state) +{ +#if (BT_TRACE_VERBOSE == TRUE) + switch (state) + { + case BTM_PAIR_STATE_IDLE: return("IDLE"); + case BTM_PAIR_STATE_GET_REM_NAME: return("GET_REM_NAME"); + case BTM_PAIR_STATE_WAIT_PIN_REQ: return("WAIT_PIN_REQ"); + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: return("WAIT_LOCAL_PIN"); + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: return("WAIT_NUM_CONFIRM"); + case BTM_PAIR_STATE_KEY_ENTRY: return("KEY_ENTRY"); + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: return("WAIT_LOCAL_OOB_RSP"); + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: return("WAIT_LOCAL_IOCAPS"); + case BTM_PAIR_STATE_INCOMING_SSP: return("INCOMING_SSP"); + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: return("WAIT_AUTH_COMPLETE"); + case BTM_PAIR_STATE_WAIT_DISCONNECT: return("WAIT_DISCONNECT"); + } + + return("???"); +#else + sprintf(btm_cb.state_temp_buffer,"%hu",state); + + return(btm_cb.state_temp_buffer); +#endif +} +#endif + + +/******************************************************************************* +** +** Function btm_sec_dev_rec_cback_event +** +** Description This function calls the callback function with the given +** result and clear the callback function. +** +** Parameters: void +** +*******************************************************************************/ +void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res) +{ + tBTM_SEC_CALLBACK *p_callback = p_dev_rec->p_callback; + + if (p_dev_rec->p_callback) + { + p_dev_rec->p_callback = NULL; + + (*p_callback) (p_dev_rec->bd_addr, p_dev_rec->p_ref_data, res); + + } + btm_sec_check_pending_reqs(); +} + +/******************************************************************************* +** +** Function btm_sec_queue_mx_request +** +** Description Return state description for tracing +** +*******************************************************************************/ +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + + p_e = (tBTM_SEC_QUEUE_ENTRY *)GKI_getbuf (sizeof(tBTM_SEC_QUEUE_ENTRY)); + + if (p_e) + { + p_e->psm = psm; + p_e->is_orig = is_orig; + p_e->p_callback = p_callback; + p_e->p_ref_data = p_ref_data; + p_e->mx_proto_id = mx_proto_id; + p_e->mx_chan_id = mx_chan_id; + + memcpy (p_e->bd_addr, bd_addr, BD_ADDR_LEN); + + BTM_TRACE_EVENT4 ("btm_sec_queue_mx_request() PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u", + psm, is_orig, mx_proto_id, mx_chan_id); + + GKI_enqueue (&btm_cb.sec_pending_q, p_e); + + return(TRUE); + } + + return(FALSE); +} + +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 major = (UINT8)(p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK); + UINT8 minor = (UINT8)(p_dev_rec->dev_class[2] & BTM_COD_MINOR_CLASS_MASK); + BOOLEAN rv = FALSE; + + if ((major == BTM_COD_MAJOR_AUDIO) + && ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) || (minor == BTM_COD_MINOR_CAR_AUDIO)) ) + { + BTM_TRACE_EVENT2 ("btm_sec_check_prefetch_pin: Skipping pre-fetch PIN for carkit COD Major: 0x%02x Minor: 0x%02x", major, minor); + + if (btm_cb.security_mode_changed == FALSE) + { + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if(!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + btsnd_hcic_write_auth_enable (TRUE); + } + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + + /* If we got a PIN, use that, else try to get one */ + if (btm_cb.pin_code_len) + { + BTM_PINCodeReply (p_dev_rec->bd_addr, BTM_SUCCESS, btm_cb.pin_code_len, btm_cb.pin_code, p_dev_rec->trusted_mask); + } + else + { + /* pin was not supplied - pre-fetch pin code now */ + if (btm_cb.api.p_pin_callback && ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0)) + { + BTM_TRACE_DEBUG0("btm_sec_check_prefetch_pin: PIN code callback called"); + if (btm_bda_to_acl(p_dev_rec->bd_addr) == NULL) + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + (btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + } + + rv = TRUE; + } + + return rv; +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_sec_clear_ble_keys +** +** Description This function is called to clear out the BLE keys. +** Typically when devices are removed in BTM_SecDeleteDevice, +** or when a new BT Link key is generated. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) +{ + + BTM_TRACE_DEBUG0 ("btm_sec_clear_ble_keys: Clearing BLE Keys"); +#if (SMP_INCLUDED== TRUE) + p_dev_rec->ble.key_type = 0; + memset (&p_dev_rec->ble.keys, 0, sizeof(tBTM_SEC_BLE_KEYS)); +#endif + gatt_delete_dev_from_srv_chg_clt_list(p_dev_rec->bd_addr); +} + + +/******************************************************************************* +** +** Function btm_sec_is_a_bonded_dev +** +** Description Is the specified device is a bonded device +** +** Returns TRUE - dev is bonded +** +*******************************************************************************/ +BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda) +{ + + tBTM_SEC_DEV_REC *p_dev_rec= btm_find_dev (bda); + BOOLEAN is_bonded= FALSE; + +#if (SMP_INCLUDED== TRUE) + if (p_dev_rec && (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN))) + { + is_bonded = TRUE; + } +#endif + BTM_TRACE_DEBUG1 ("btm_sec_is_a_bonded_dev is_bonded=%d", is_bonded); + return(is_bonded); +} + +/******************************************************************************* +** +** Function btm_sec_find_bonded_dev +** +** Description Find a bonded device starting from the specified index +** +** Returns TRUE - found a bonded device +** +*******************************************************************************/ +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_DEV_REC *p_rec) +{ + BOOLEAN found= FALSE; + +#if (SMP_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + if (start_idx >= BTM_SEC_MAX_DEVICE_RECORDS) + { + BTM_TRACE_DEBUG0 ("LE bonded device not found"); + return found; + } + + p_dev_rec = &btm_cb.sec_dev_rec[start_idx]; + for (i = start_idx; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) + { + *p_found_idx = i; + p_rec = p_dev_rec; + break; + } + } + BTM_TRACE_DEBUG1 ("btm_sec_find_bonded_dev=%d", found); +#endif + return(found); +} +#endif /* BLE_INCLUDED */ + diff --git a/stack/btu/btu_hcif.c b/stack/btu/btu_hcif.c new file mode 100644 index 0000000..8ca9d35 --- /dev/null +++ b/stack/btu/btu_hcif.c @@ -0,0 +1,2257 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that interface with the HCI transport. On + * the receive side, it routes events to the appropriate handler, e.g. + * L2CAP, ScoMgr. On the transmit side, it manages the command + * transmission. + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "l2c_int.h" +#include "btm_api.h" +#include "btm_int.h" + +// btla-specific ++ +#define LOG_TAG "BTLD" +#if (defined(ANDROID_APP_INCLUDED) && (ANDROID_APP_INCLUDED == TRUE) && (!defined(LINUX_NATIVE)) ) +#include +#else +#define LOGV(format, ...) fprintf (stdout, LOG_TAG format"\n", ## __VA_ARGS__) +#define LOGE(format, ...) fprintf (stderr, LOG_TAG format"\n", ## __VA_ARGS__) +#define LOGI(format, ...) fprintf (stdout, LOG_TAG format"\n", ## __VA_ARGS__) +#endif + +// btla-specific ++ +/* BTE application task */ +#if APPL_INCLUDED == TRUE +#include "bte_appl.h" +#endif +// btla-specific -- + +/********************************************************************************/ +/* 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 btu_hcif_inquiry_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_inquiry_result_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_inquiry_rssi_result_evt (UINT8 *p, UINT16 evt_len); +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +static void btu_hcif_extended_inquiry_result_evt (UINT8 *p, UINT16 evt_len); +#endif + +static void btu_hcif_connection_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_connection_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_disconnection_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_authentication_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_encryption_change_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_change_conn_link_key_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_master_link_key_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_read_rmt_features_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_read_rmt_version_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_qos_setup_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_command_complete_evt (UINT8 controller_id, UINT8 *p, UINT16 evt_len); +static void btu_hcif_command_status_evt (UINT8 controller_id, UINT8 *p, UINT16 evt_len); +static void btu_hcif_hardware_error_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_flush_occured_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_role_change_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_num_compl_data_pkts_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_mode_change_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_return_link_keys_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_pin_code_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_link_key_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_link_key_notification_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_loopback_command_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_data_buf_overflow_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_max_slots_changed_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_read_clock_off_comp_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_conn_pkt_type_change_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_qos_violation_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_page_scan_mode_change_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_page_scan_rep_mode_chng_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_esco_connection_comp_evt(UINT8 *p, UINT16 evt_len); +static void btu_hcif_esco_connection_chg_evt(UINT8 *p, UINT16 evt_len); + +/* Simple Pairing Events */ +static void btu_hcif_host_support_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_io_cap_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_io_cap_response_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_user_conf_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_user_passkey_request_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_user_passkey_notif_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_keypress_notif_evt (UINT8 *p, UINT16 evt_len); +static void btu_hcif_link_super_tout_evt (UINT8 *p, UINT16 evt_len); + + #if BTM_OOB_INCLUDED == TRUE +static void btu_hcif_rem_oob_request_evt (UINT8 *p, UINT16 evt_len); + #endif + +static void btu_hcif_simple_pair_complete_evt (UINT8 *p, UINT16 evt_len); + #if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +static void btu_hcif_enhanced_flush_complete_evt (UINT8 *p, UINT16 evt_len); + #endif + + #if (BTM_SSR_INCLUDED == TRUE) +static void btu_hcif_ssr_evt (UINT8 *p, UINT16 evt_len); + #endif /* BTM_SSR_INCLUDED == TRUE */ + + #if (HID_DEV_INCLUDED == TRUE) && (HID_DEV_PM_INCLUDED == TRUE) +extern void hidd_pm_proc_mode_change( UINT8 hci_status, UINT8 mode, UINT16 interval ); + #endif + + + #if BLE_INCLUDED == TRUE +static void btu_ble_ll_conn_complete_evt (UINT8 *p, UINT16 evt_len); +static void btu_ble_process_adv_pkt (UINT8 *p, UINT16 evt_len); +static void btu_ble_read_remote_feat_evt (UINT8 *p, UINT16 evt_len); +static void btu_ble_ll_conn_param_upd_evt (UINT8 *p, UINT16 evt_len); +static void btu_ble_proc_ltk_req (UINT8 *p, UINT16 evt_len); +static void btu_hcif_encyption_key_refresh_cmpl_evt (UINT8 *p, UINT16 evt_len); + #endif +/******************************************************************************* +** +** Function btu_hcif_store_cmd +** +** Description This function stores a copy of an outgoing command and +** and sets a timer waiting for a event in response to the +** command. +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_store_cmd (UINT8 controller_id, BT_HDR *p_buf) +{ + tHCI_CMD_CB * p_hci_cmd_cb = &(btu_cb.hci_cmd_cb[controller_id]); + UINT16 opcode; + BT_HDR *p_cmd; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* get command opcode */ + STREAM_TO_UINT16 (opcode, p); + + /* don't do anything for certain commands */ + if ((opcode == HCI_RESET) || (opcode == HCI_HOST_NUM_PACKETS_DONE)) + { + return; + } + + /* allocate buffer (HCI_GET_CMD_BUF will either get a buffer from HCI_CMD_POOL or from 'best-fit' pool) */ + if ((p_cmd = HCI_GET_CMD_BUF(p_buf->len + p_buf->offset - HCIC_PREAMBLE_SIZE)) == NULL) + { + return; + } + + /* copy buffer */ + memcpy (p_cmd, p_buf, sizeof(BT_HDR)); + + /* If vendor specific save the callback function */ + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC +#if BLE_INCLUDED == TRUE + || (opcode == HCI_BLE_RAND ) + || (opcode == HCI_BLE_ENCRYPT) +#endif + ) + { +#if 0 + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_DEBUG, + "Storing VSC callback opcode=0x%04x, Callback function=0x%07x", + opcode, *(UINT32 *)(p_buf + 1)); +#endif + memcpy ((UINT8 *)(p_cmd + 1), (UINT8 *)(p_buf + 1), sizeof(void *)); + } + + memcpy ((UINT8 *)(p_cmd + 1) + p_cmd->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + /* queue copy of cmd */ + GKI_enqueue(&(p_hci_cmd_cb->cmd_cmpl_q), p_cmd); + + /* start timer */ + if (BTU_CMD_CMPL_TIMEOUT > 0) + { +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + p_hci_cmd_cb->checked_hcisu = FALSE; +#endif + btu_start_timer (&(p_hci_cmd_cb->cmd_cmpl_timer), + (UINT16)(BTU_TTYPE_BTU_CMD_CMPL + controller_id), + BTU_CMD_CMPL_TIMEOUT); + } +} + +/******************************************************************************* +** +** Function btu_hcif_process_event +** +** Description This function is called when an event is received from +** the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_process_event (UINT8 controller_id, BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 hci_evt_code, hci_evt_len; +#if BLE_INCLUDED == TRUE + UINT8 ble_sub_code; +#endif + STREAM_TO_UINT8 (hci_evt_code, p); + STREAM_TO_UINT8 (hci_evt_len, p); + + switch (hci_evt_code) + { + case HCI_INQUIRY_COMP_EVT: + btu_hcif_inquiry_comp_evt (p, hci_evt_len); + break; + case HCI_INQUIRY_RESULT_EVT: + btu_hcif_inquiry_result_evt (p, hci_evt_len); + break; + case HCI_INQUIRY_RSSI_RESULT_EVT: + btu_hcif_inquiry_rssi_result_evt (p, hci_evt_len); + break; +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + case HCI_EXTENDED_INQUIRY_RESULT_EVT: + btu_hcif_extended_inquiry_result_evt (p, hci_evt_len); + break; +#endif + case HCI_CONNECTION_COMP_EVT: + btu_hcif_connection_comp_evt (p, hci_evt_len); + break; + case HCI_CONNECTION_REQUEST_EVT: + btu_hcif_connection_request_evt (p, hci_evt_len); + break; + case HCI_DISCONNECTION_COMP_EVT: + btu_hcif_disconnection_comp_evt (p, hci_evt_len); + break; + case HCI_AUTHENTICATION_COMP_EVT: + btu_hcif_authentication_comp_evt (p, hci_evt_len); + break; + case HCI_RMT_NAME_REQUEST_COMP_EVT: + btu_hcif_rmt_name_request_comp_evt (p, hci_evt_len); + break; + case HCI_ENCRYPTION_CHANGE_EVT: + btu_hcif_encryption_change_evt (p, hci_evt_len); + break; +#if BLE_INCLUDED == TRUE + case HCI_ENCRYPTION_KEY_REFRESH_COMP_EVT: + btu_hcif_encyption_key_refresh_cmpl_evt(p, hci_evt_len); + break; +#endif + case HCI_CHANGE_CONN_LINK_KEY_EVT: + btu_hcif_change_conn_link_key_evt (p, hci_evt_len); + break; + case HCI_MASTER_LINK_KEY_COMP_EVT: + btu_hcif_master_link_key_comp_evt (p, hci_evt_len); + break; + case HCI_READ_RMT_FEATURES_COMP_EVT: + btu_hcif_read_rmt_features_comp_evt (p, hci_evt_len); + break; + case HCI_READ_RMT_EXT_FEATURES_COMP_EVT: + btu_hcif_read_rmt_ext_features_comp_evt (p, hci_evt_len); + break; + case HCI_READ_RMT_VERSION_COMP_EVT: + btu_hcif_read_rmt_version_comp_evt (p, hci_evt_len); + break; + case HCI_QOS_SETUP_COMP_EVT: + btu_hcif_qos_setup_comp_evt (p, hci_evt_len); + break; + case HCI_COMMAND_COMPLETE_EVT: + btu_hcif_command_complete_evt (controller_id, p, hci_evt_len); + break; + case HCI_COMMAND_STATUS_EVT: + btu_hcif_command_status_evt (controller_id, p, hci_evt_len); + break; + case HCI_HARDWARE_ERROR_EVT: + btu_hcif_hardware_error_evt (p, hci_evt_len); + break; + case HCI_FLUSH_OCCURED_EVT: + btu_hcif_flush_occured_evt (p, hci_evt_len); + break; + case HCI_ROLE_CHANGE_EVT: + btu_hcif_role_change_evt (p, hci_evt_len); + break; + case HCI_NUM_COMPL_DATA_PKTS_EVT: + btu_hcif_num_compl_data_pkts_evt (p, hci_evt_len); + break; + case HCI_MODE_CHANGE_EVT: + btu_hcif_mode_change_evt (p, hci_evt_len); + break; + case HCI_RETURN_LINK_KEYS_EVT: + btu_hcif_return_link_keys_evt (p, hci_evt_len); + break; + case HCI_PIN_CODE_REQUEST_EVT: + btu_hcif_pin_code_request_evt (p, hci_evt_len); + break; + case HCI_LINK_KEY_REQUEST_EVT: + btu_hcif_link_key_request_evt (p, hci_evt_len); + break; + case HCI_LINK_KEY_NOTIFICATION_EVT: + btu_hcif_link_key_notification_evt (p, hci_evt_len); + break; + case HCI_LOOPBACK_COMMAND_EVT: + btu_hcif_loopback_command_evt (p, hci_evt_len); + break; + case HCI_DATA_BUF_OVERFLOW_EVT: + btu_hcif_data_buf_overflow_evt (p, hci_evt_len); + break; + case HCI_MAX_SLOTS_CHANGED_EVT: + btu_hcif_max_slots_changed_evt (p, hci_evt_len); + break; + case HCI_READ_CLOCK_OFF_COMP_EVT: + btu_hcif_read_clock_off_comp_evt (p, hci_evt_len); + break; + case HCI_CONN_PKT_TYPE_CHANGE_EVT: + btu_hcif_conn_pkt_type_change_evt (p, hci_evt_len); + break; + case HCI_QOS_VIOLATION_EVT: + btu_hcif_qos_violation_evt (p, hci_evt_len); + break; + case HCI_PAGE_SCAN_MODE_CHANGE_EVT: + btu_hcif_page_scan_mode_change_evt (p, hci_evt_len); + break; + case HCI_PAGE_SCAN_REP_MODE_CHNG_EVT: + btu_hcif_page_scan_rep_mode_chng_evt (p, hci_evt_len); + break; + case HCI_ESCO_CONNECTION_COMP_EVT: + btu_hcif_esco_connection_comp_evt (p, hci_evt_len); + break; + case HCI_ESCO_CONNECTION_CHANGED_EVT: + btu_hcif_esco_connection_chg_evt (p, hci_evt_len); + break; +#if (BTM_SSR_INCLUDED == TRUE) + case HCI_SNIFF_SUB_RATE_EVT: + btu_hcif_ssr_evt (p, hci_evt_len); + break; +#endif /* BTM_SSR_INCLUDED == TRUE */ + case HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT: + btu_hcif_host_support_evt (p, hci_evt_len); + break; + case HCI_IO_CAPABILITY_REQUEST_EVT: + btu_hcif_io_cap_request_evt (p, hci_evt_len); + break; + case HCI_IO_CAPABILITY_RESPONSE_EVT: + btu_hcif_io_cap_response_evt (p, hci_evt_len); + break; + case HCI_USER_CONFIRMATION_REQUEST_EVT: + btu_hcif_user_conf_request_evt (p, hci_evt_len); + break; + case HCI_USER_PASSKEY_REQUEST_EVT: + btu_hcif_user_passkey_request_evt (p, hci_evt_len); + break; +#if BTM_OOB_INCLUDED == TRUE + case HCI_REMOTE_OOB_DATA_REQUEST_EVT: + btu_hcif_rem_oob_request_evt (p, hci_evt_len); + break; +#endif + case HCI_SIMPLE_PAIRING_COMPLETE_EVT: + btu_hcif_simple_pair_complete_evt (p, hci_evt_len); + break; + case HCI_USER_PASSKEY_NOTIFY_EVT: + btu_hcif_user_passkey_notif_evt (p, hci_evt_len); + break; + case HCI_KEYPRESS_NOTIFY_EVT: + btu_hcif_keypress_notif_evt (p, hci_evt_len); + break; + case HCI_LINK_SUPER_TOUT_CHANGED_EVT: + btu_hcif_link_super_tout_evt (p, hci_evt_len); + break; +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + case HCI_ENHANCED_FLUSH_COMPLETE_EVT: + btu_hcif_enhanced_flush_complete_evt (p, hci_evt_len); + break; +#endif + +#if (BLE_INCLUDED == TRUE) + case HCI_BLE_EVENT: + STREAM_TO_UINT8 (ble_sub_code, p); + + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_EVENT, "BLE HCI(id=%d) event = 0x%02x)", + hci_evt_code, ble_sub_code); + + switch (ble_sub_code) + { + case HCI_BLE_ADV_PKT_RPT_EVT: /* result of inquiry */ + btu_ble_process_adv_pkt(p, hci_evt_len); + break; + case HCI_BLE_CONN_COMPLETE_EVT: + btu_ble_ll_conn_complete_evt(p, hci_evt_len); + break; + case HCI_BLE_LL_CONN_PARAM_UPD_EVT: + btu_ble_ll_conn_param_upd_evt(p, hci_evt_len); + break; + case HCI_BLE_READ_REMOTE_FEAT_CMPL_EVT: + btu_ble_read_remote_feat_evt(p, hci_evt_len); + break; + case HCI_BLE_LTK_REQ_EVT: /* received only at slave device */ + btu_ble_proc_ltk_req(p, hci_evt_len); + break; + } + break; +#endif /* BLE_INCLUDED */ + case HCI_VENDOR_SPECIFIC_EVT: + btm_vendor_specific_evt (p, hci_evt_len); + break; + } +} + + +/******************************************************************************* +** +** Function btu_hcif_send_cmd +** +** Description This function is called to check if it can send commands +** to the Host Controller. It may be passed the address of +** a packet to send. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_send_cmd (UINT8 controller_id, BT_HDR *p_buf) +{ + tHCI_CMD_CB * p_hci_cmd_cb = &(btu_cb.hci_cmd_cb[controller_id]); + +#if ((L2CAP_HOST_FLOW_CTRL == TRUE)||defined(HCI_TESTER)) + UINT8 *pp; + UINT16 code; +#endif + + /* If there are already commands in the queue, then enqueue this command */ + if ((p_buf) && (p_hci_cmd_cb->cmd_xmit_q.count)) + { + GKI_enqueue (&(p_hci_cmd_cb->cmd_xmit_q), p_buf); + p_buf = NULL; + } + + /* Allow for startup case, where no acks may be received */ + if ( ((controller_id == LOCAL_BR_EDR_CONTROLLER_ID) + && (p_hci_cmd_cb->cmd_window == 0) + && (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT)) ) + { + p_hci_cmd_cb->cmd_window = p_hci_cmd_cb->cmd_xmit_q.count + 1; + } + + /* See if we can send anything */ + while (p_hci_cmd_cb->cmd_window != 0) + { + if (!p_buf) + p_buf = (BT_HDR *)GKI_dequeue (&(p_hci_cmd_cb->cmd_xmit_q)); + + if (p_buf) + { + btu_hcif_store_cmd(controller_id, p_buf); + +#if ((L2CAP_HOST_FLOW_CTRL == TRUE)||defined(HCI_TESTER)) + pp = (UINT8 *)(p_buf + 1) + p_buf->offset; + + STREAM_TO_UINT16 (code, pp); + + /* + * We do not need to decrease window for host flow control, + * host flow control does not receive an event back from controller + */ + if (code != HCI_HOST_NUM_PACKETS_DONE) +#endif + p_hci_cmd_cb->cmd_window--; + + if (controller_id == LOCAL_BR_EDR_CONTROLLER_ID) + { + HCI_CMD_TO_LOWER(p_buf); + } + else + { + /* Unknown controller */ + BT_TRACE_1 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, "BTU HCI(ctrl id=%d) controller ID not recognized", controller_id); + GKI_freebuf(p_buf);; + } + + p_buf = NULL; + } + else + break; + } + + if (p_buf) + GKI_enqueue (&(p_hci_cmd_cb->cmd_xmit_q), p_buf); + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + if (controller_id == LOCAL_BR_EDR_CONTROLLER_ID) + { + /* check if controller can go to sleep */ + btu_check_bt_sleep (); + } +#endif + +} + + +/******************************************************************************* +** +** Function btu_hcif_send_host_rdy_for_data +** +** Description This function is called to check if it can send commands +** to the Host Controller. It may be passed the address of +** a packet to send. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_send_host_rdy_for_data(void) +{ + UINT16 num_pkts[MAX_L2CAP_LINKS + 4]; /* 3 SCO connections */ + UINT16 handles[MAX_L2CAP_LINKS + 4]; + UINT8 num_ents; + + /* Get the L2CAP numbers */ + num_ents = l2c_link_pkts_rcvd (num_pkts, handles); + + /* Get the SCO numbers */ + /* No SCO for now ?? */ + + if (num_ents) + { + btsnd_hcic_host_num_xmitted_pkts (num_ents, handles, num_pkts); + } +} + +/******************************************************************************* +** +** Function btu_hcif_inquiry_comp_evt +** +** Description Process event HCI_INQUIRY_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + /* Tell inquiry processing that we are done */ + btm_process_inq_complete(status, BTM_BR_INQUIRY_MASK); +} + + +/******************************************************************************* +** +** Function btu_hcif_inquiry_result_evt +** +** Description Process event HCI_INQUIRY_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_result_evt (UINT8 *p, UINT16 evt_len) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_STANDARD); +} + +/******************************************************************************* +** +** Function btu_hcif_inquiry_rssi_result_evt +** +** Description Process event HCI_INQUIRY_RSSI_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_inquiry_rssi_result_evt (UINT8 *p, UINT16 evt_len) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_WITH_RSSI); +} + +/******************************************************************************* +** +** Function btu_hcif_extended_inquiry_result_evt +** +** Description Process event HCI_EXTENDED_INQUIRY_RESULT_EVT +** +** Returns void +** +*******************************************************************************/ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +static void btu_hcif_extended_inquiry_result_evt (UINT8 *p, UINT16 evt_len) +{ + /* Store results in the cache */ + btm_process_inq_results (p, BTM_INQ_RESULT_EXTENDED); +} +#endif + +/******************************************************************************* +** +** Function btu_hcif_connection_comp_evt +** +** Description Process event HCI_CONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_connection_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + BD_ADDR bda; + UINT8 link_type; + UINT8 enc_mode; +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA esco_data; +#endif + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (link_type, p); + STREAM_TO_UINT8 (enc_mode, p); + + handle = HCID_GET_HANDLE (handle); + + if (link_type == HCI_LINK_TYPE_ACL) + { + btm_sec_connected (bda, handle, status, enc_mode); + + l2c_link_hci_conn_comp (status, handle, bda); + } +#if BTM_SCO_INCLUDED == TRUE + else + { + memset(&esco_data, 0, sizeof(tBTM_ESCO_DATA)); + /* esco_data.link_type = HCI_LINK_TYPE_SCO; already zero */ + memcpy (esco_data.bd_addr, bda, BD_ADDR_LEN); + btm_sco_connected (status, bda, handle, &esco_data); + } +#endif /* BTM_SCO_INCLUDED */ +} + + +/******************************************************************************* +** +** Function btu_hcif_connection_request_evt +** +** Description Process event HCI_CONNECTION_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_connection_request_evt (UINT8 *p, UINT16 evt_len) +{ + BD_ADDR bda; + DEV_CLASS dc; + UINT8 link_type; + + STREAM_TO_BDADDR (bda, p); + STREAM_TO_DEVCLASS (dc, p); + STREAM_TO_UINT8 (link_type, p); + + /* Pass request to security manager to check connect filters before */ + /* passing request to l2cap */ + if (link_type == HCI_LINK_TYPE_ACL) + { + btm_sec_conn_req (bda, dc); + } +#if BTM_SCO_INCLUDED == TRUE + else + { + btm_sco_conn_req (bda, dc, link_type); + } +#endif /* BTM_SCO_INCLUDED */ +} + + +/******************************************************************************* +** +** Function btu_hcif_disconnection_comp_evt +** +** Description Process event HCI_DISCONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_disconnection_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT8 reason; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (reason, p); + + handle = HCID_GET_HANDLE (handle); + +#if BTM_SCO_INCLUDED == TRUE + /* If L2CAP doesn't know about it, send it to SCO */ + if (!l2c_link_hci_disc_comp (handle, reason)) + btm_sco_removed (handle, reason); +#else + l2c_link_hci_disc_comp (handle, reason); +#endif /* BTM_SCO_INCLUDED */ + + /* Notify security manager */ + btm_sec_disconnected (handle, reason); +} + +/******************************************************************************* +** +** Function btu_hcif_authentication_comp_evt +** +** Description Process event HCI_AUTHENTICATION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_authentication_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + btm_sec_auth_complete (handle, status); +} + + +/******************************************************************************* +** +** Function btu_hcif_rmt_name_request_comp_evt +** +** Description Process event HCI_RMT_NAME_REQUEST_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + BD_ADDR bd_addr; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_BDADDR (bd_addr, p); + + evt_len -= (1 + BD_ADDR_LEN); + + btm_process_remote_name (bd_addr, p, evt_len, status); + + btm_sec_rmt_name_request_complete (bd_addr, p, status); +} + + +/******************************************************************************* +** +** Function btu_hcif_encryption_change_evt +** +** Description Process event HCI_ENCRYPTION_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_encryption_change_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT8 encr_enable; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (encr_enable, p); + + btm_acl_encrypt_change (handle, status, encr_enable); + btm_sec_encrypt_change (handle, status, encr_enable); +} + + +/******************************************************************************* +** +** Function btu_hcif_change_conn_link_key_evt +** +** Description Process event HCI_CHANGE_CONN_LINK_KEY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_change_conn_link_key_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + btm_acl_link_key_change (handle, status); +} + + +/******************************************************************************* +** +** Function btu_hcif_master_link_key_comp_evt +** +** Description Process event HCI_MASTER_LINK_KEY_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_master_link_key_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT8 key_flg; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (key_flg, p); + + btm_sec_mkey_comp_event (handle, status, key_flg); +} + + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_features_comp_evt +** +** Description Process event HCI_READ_RMT_FEATURES_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_features_comp_evt (UINT8 *p, UINT16 evt_len) +{ + btm_read_remote_features_complete(p); +} + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_ext_features_comp_evt +** +** Description Process event HCI_READ_RMT_EXT_FEATURES_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p, UINT16 evt_len) +{ + /* Status is in first byte of stream */ + if (*p == HCI_SUCCESS) + btm_read_remote_ext_features_complete(p); + else + btm_read_remote_ext_features_failed(*p); +} + +/******************************************************************************* +** +** Function btu_hcif_read_rmt_version_comp_evt +** +** Description Process event HCI_READ_RMT_VERSION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_rmt_version_comp_evt (UINT8 *p, UINT16 evt_len) +{ + btm_read_remote_version_complete (p); +} + + +/******************************************************************************* +** +** Function btu_hcif_qos_setup_comp_evt +** +** Description Process event HCI_QOS_SETUP_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_qos_setup_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + FLOW_SPEC flow; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (flow.qos_flags, p); + STREAM_TO_UINT8 (flow.service_type, p); + STREAM_TO_UINT32 (flow.token_rate, p); + STREAM_TO_UINT32 (flow.peak_bandwidth, p); + STREAM_TO_UINT32 (flow.latency, p); + STREAM_TO_UINT32 (flow.delay_variation, p); + + btm_qos_setup_complete(status, handle, &flow); +} + + +/******************************************************************************* +** +** Function btu_hcif_esco_connection_comp_evt +** +** Description Process event HCI_ESCO_CONNECTION_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_esco_connection_comp_evt (UINT8 *p, UINT16 evt_len) +{ +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA data; + UINT16 handle; + BD_ADDR bda; + UINT8 status; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_BDADDR (bda, p); + + STREAM_TO_UINT8 (data.link_type, p); + STREAM_TO_UINT8 (data.tx_interval, p); + STREAM_TO_UINT8 (data.retrans_window, p); + STREAM_TO_UINT16 (data.rx_pkt_len, p); + STREAM_TO_UINT16 (data.tx_pkt_len, p); + STREAM_TO_UINT8 (data.air_mode, p); + + memcpy (data.bd_addr, bda, BD_ADDR_LEN); + btm_sco_connected (status, bda, handle, &data); +#endif +} + + +/******************************************************************************* +** +** Function btu_hcif_esco_connection_chg_evt +** +** Description Process event HCI_ESCO_CONNECTION_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_esco_connection_chg_evt (UINT8 *p, UINT16 evt_len) +{ +#if BTM_SCO_INCLUDED == TRUE + UINT16 handle; + UINT16 tx_pkt_len; + UINT16 rx_pkt_len; + UINT8 status; + UINT8 tx_interval; + UINT8 retrans_window; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + STREAM_TO_UINT8 (tx_interval, p); + STREAM_TO_UINT8 (retrans_window, p); + STREAM_TO_UINT16 (rx_pkt_len, p); + STREAM_TO_UINT16 (tx_pkt_len, p); + + btm_esco_proc_conn_chg (status, handle, tx_interval, retrans_window, + rx_pkt_len, tx_pkt_len); +#endif +} + +/******************************************************************************* +** +** Function btu_hcif_hdl_command_complete +** +** Description Handle command complete event +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hdl_command_complete (UINT16 opcode, UINT8 *p, UINT16 evt_len, + void *p_cplt_cback, UINT8 ctr_id) +{ + switch (opcode) + { + case HCI_RESET: + btm_reset_complete (); /* BR/EDR */ + break; + + case HCI_INQUIRY_CANCEL: + /* Tell inquiry processing that we are done */ + btm_process_cancel_complete(HCI_SUCCESS, BTM_BR_INQUIRY_MASK); + break; + case HCI_SET_EVENT_FILTER: + btm_event_filter_complete (p); + break; + + case HCI_READ_STORED_LINK_KEY: + btm_read_stored_link_key_complete (p); + break; + + case HCI_WRITE_STORED_LINK_KEY: + btm_write_stored_link_key_complete (p); + break; + + case HCI_DELETE_STORED_LINK_KEY: + btm_delete_stored_link_key_complete (p); + break; + + case HCI_READ_LOCAL_VERSION_INFO: + btm_read_local_version_complete (p, evt_len); + break; + + case HCI_READ_POLICY_SETTINGS: + btm_read_link_policy_complete (p); + break; + + case HCI_READ_BUFFER_SIZE: + btm_read_hci_buf_size_complete (p, evt_len); + break; + + case HCI_READ_LOCAL_FEATURES: + btm_read_local_features_complete (p, evt_len); + break; + + case HCI_READ_LOCAL_NAME: + btm_read_local_name_complete (p, evt_len); + break; + + case HCI_READ_BD_ADDR: + btm_read_local_addr_complete (p, evt_len); + break; + + case HCI_GET_LINK_QUALITY: + btm_read_link_quality_complete (p); + break; + + case HCI_READ_RSSI: + btm_read_rssi_complete (p); + break; + + case HCI_READ_TRANSMIT_POWER_LEVEL: + btm_read_tx_power_complete(p, FALSE); + break; + + case HCI_CREATE_CONNECTION_CANCEL: + btm_create_conn_cancel_complete(p); + break; + + case HCI_READ_LOCAL_OOB_DATA: +#if BTM_OOB_INCLUDED == TRUE + btm_read_local_oob_complete(p); +#endif + break; + + + case HCI_READ_INQ_TX_POWER_LEVEL: + btm_read_linq_tx_power_complete (p); + break; + +#if (BLE_INCLUDED == TRUE) +/* BLE Commands */ + case HCI_BLE_READ_WHITE_LIST_SIZE : + btm_read_white_list_size_complete(p, evt_len); + break; + + case HCI_BLE_ADD_WHITE_LIST: + btm_ble_add_2_white_list_complete(p, evt_len); + break; + + case HCI_BLE_CLEAR_WHITE_LIST: + btm_ble_clear_white_list_complete(p, evt_len); + break; + + case HCI_BLE_REMOVE_WHITE_LIST: + btm_ble_remove_from_white_list_complete(p, evt_len); + break; + + case HCI_BLE_RAND: + case HCI_BLE_ENCRYPT: + + btm_ble_rand_enc_complete (p, opcode, (tBTM_RAND_ENC_CB *)p_cplt_cback); + break; + case HCI_BLE_READ_BUFFER_SIZE: + btm_read_ble_buf_size_complete(p, evt_len); + break; + + case HCI_BLE_READ_ADV_CHNL_TX_POWER: + btm_read_tx_power_complete(p, TRUE); + break; + + case HCI_BLE_WRITE_ADV_ENABLE: + btm_ble_write_adv_enable_complete(p); + break; + +#endif /* (BLE_INCLUDED == TRUE) */ + + default: + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) + btm_vsc_complete (p, opcode, evt_len, (tBTM_CMPL_CB *)p_cplt_cback); + break; + } +} + +/******************************************************************************* +** +** Function btu_hcif_command_complete_evt +** +** Description Process event HCI_COMMAND_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_command_complete_evt (UINT8 controller_id, UINT8 *p, UINT16 evt_len) +{ + tHCI_CMD_CB *p_hci_cmd_cb = &(btu_cb.hci_cmd_cb[controller_id]); + UINT16 cc_opcode; + BT_HDR *p_cmd; + void *p_cplt_cback = NULL; + + STREAM_TO_UINT8 (p_hci_cmd_cb->cmd_window, p); + +#if (defined(HCI_MAX_SIMUL_CMDS) && (HCI_MAX_SIMUL_CMDS > 0)) + if (p_hci_cmd_cb->cmd_window > HCI_MAX_SIMUL_CMDS) + p_hci_cmd_cb->cmd_window = HCI_MAX_SIMUL_CMDS; +#endif + + STREAM_TO_UINT16 (cc_opcode, p); + + evt_len -= 3; + + /* only do this for certain commands */ + if ((cc_opcode != HCI_RESET) && (cc_opcode != HCI_HOST_NUM_PACKETS_DONE) && + (cc_opcode != HCI_COMMAND_NONE)) + { + /* dequeue and free stored command */ + +/* always use cmd code check, when one cmd timeout waiting for cmd_cmpl, + it'll cause the rest of the command goes in wrong order */ + p_cmd = (BT_HDR *) GKI_getfirst (&p_hci_cmd_cb->cmd_cmpl_q); + + while (p_cmd) + { + UINT16 opcode_dequeued; + UINT8 *p_dequeued; + + /* Make sure dequeued command is for the command_cplt received */ + p_dequeued = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + STREAM_TO_UINT16 (opcode_dequeued, p_dequeued); + + if (opcode_dequeued != cc_opcode) + { + /* opcode does not match, check next command in the queue */ + p_cmd = (BT_HDR *) GKI_getnext(p_cmd); + continue; + } + GKI_remove_from_queue(&p_hci_cmd_cb->cmd_cmpl_q, p_cmd); + + /* If command was a VSC, then extract command_complete callback */ + if ((cc_opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC +#if BLE_INCLUDED == TRUE + || (cc_opcode == HCI_BLE_RAND ) + || (cc_opcode == HCI_BLE_ENCRYPT) +#endif + ) + { + p_cplt_cback = *((void **)(p_cmd + 1)); + } + + GKI_freebuf (p_cmd); + + break; + } + + /* if more commands in queue restart timer */ + if (BTU_CMD_CMPL_TIMEOUT > 0) + { + if (!GKI_queue_is_empty (&(p_hci_cmd_cb->cmd_cmpl_q))) + { +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + p_hci_cmd_cb->checked_hcisu = FALSE; +#endif + btu_start_timer (&(p_hci_cmd_cb->cmd_cmpl_timer), + (UINT16)(BTU_TTYPE_BTU_CMD_CMPL + controller_id), + BTU_CMD_CMPL_TIMEOUT); + } + else + { + btu_stop_timer (&(p_hci_cmd_cb->cmd_cmpl_timer)); + } + } + } + + /* handle event */ + btu_hcif_hdl_command_complete (cc_opcode, p, evt_len, p_cplt_cback, controller_id); + + /* see if we can send more commands */ + btu_hcif_send_cmd (controller_id, NULL); +} + + +/******************************************************************************* +** +** Function btu_hcif_hdl_command_status +** +** Description Handle a command status event +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hdl_command_status (UINT16 opcode, UINT8 status, UINT8 *p_cmd, + void *p_vsc_status_cback, UINT8 controller_id) +{ + BD_ADDR bd_addr; + UINT16 handle; +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA esco_data; +#endif + +#if BTM_PWR_MGR_INCLUDED == TRUE + switch (opcode) + { + case HCI_EXIT_SNIFF_MODE: + case HCI_EXIT_PARK_MODE: +#if BTM_SCO_WAKE_PARKED_LINK == TRUE + if (status != HCI_SUCCESS) + { + /* Allow SCO initiation to continue if waiting for change mode event */ + if (p_cmd != NULL) + { + p_cmd++; /* bypass length field */ + STREAM_TO_UINT16 (handle, p_cmd); + btm_sco_chk_pend_unpark (status, handle); + } + } +#endif + /* Case Falls Through */ + + case HCI_HOLD_MODE: + case HCI_SNIFF_MODE: + case HCI_PARK_MODE: + btm_pm_proc_cmd_status(status); + break; + + default: +#endif /* BTM_PWR_MGR_INCLUDED */ + /* If command failed to start, we may need to tell BTM */ + if (status != HCI_SUCCESS) + { + switch (opcode) + { + case HCI_INQUIRY: + /* Tell inquiry processing that we are done */ + btm_process_inq_complete(status, BTM_BR_INQUIRY_MASK); + break; + + case HCI_RMT_NAME_REQUEST: + /* Tell inquiry processing that we are done */ + btm_process_remote_name (NULL, NULL, 0, status); + + btm_sec_rmt_name_request_complete (NULL, NULL, status); + break; + + case HCI_CHANGE_CONN_LINK_KEY: + /* Let host know we're done with error */ + /* read handle out of stored command */ + if (p_cmd != NULL) + { + p_cmd++; + STREAM_TO_UINT16 (handle, p_cmd); + + btm_acl_link_key_change (handle, status); + } + break; + + case HCI_QOS_SETUP_COMP_EVT: + /* Tell qos setup that we are done */ + btm_qos_setup_complete(status,0,NULL); + break; + + case HCI_SWITCH_ROLE: + /* Tell BTM that the command failed */ + /* read bd addr out of stored command */ + if (p_cmd != NULL) + { + p_cmd++; + STREAM_TO_BDADDR (bd_addr, p_cmd); + btm_acl_role_changed(status, bd_addr, BTM_ROLE_UNDEFINED); + } + else + btm_acl_role_changed(status, NULL, BTM_ROLE_UNDEFINED); + l2c_link_role_changed (NULL, BTM_ROLE_UNDEFINED, HCI_ERR_COMMAND_DISALLOWED); + break; + + case HCI_CREATE_CONNECTION: + /* read bd addr out of stored command */ + if (p_cmd != NULL) + { + p_cmd++; + STREAM_TO_BDADDR (bd_addr, p_cmd); + btm_sec_connected (bd_addr, HCI_INVALID_HANDLE, status, 0); + l2c_link_hci_conn_comp (status, HCI_INVALID_HANDLE, bd_addr); + } + break; + + case HCI_READ_RMT_EXT_FEATURES_COMP_EVT: +// btla-specific ++ + btu_hcif_read_rmt_ext_features_comp_evt (p_cmd - 3, 0); +// btla-specific -- + break; + + case HCI_AUTHENTICATION_REQUESTED: + /* Device refused to start authentication. That should be treated as authentication failure. */ + btm_sec_auth_complete (BTM_INVALID_HCI_HANDLE, status); + break; + + case HCI_SET_CONN_ENCRYPTION: + /* Device refused to start encryption. That should be treated as encryption failure. */ + btm_sec_encrypt_change (BTM_INVALID_HCI_HANDLE, status, FALSE); + break; + +#if BTM_SCO_INCLUDED == TRUE + case HCI_SETUP_ESCO_CONNECTION: + /* read handle out of stored command */ + if (p_cmd != NULL) + { + p_cmd++; + STREAM_TO_UINT16 (handle, p_cmd); + + /* Determine if initial connection failed or is a change of setup */ + if (btm_is_sco_active(handle)) + btm_esco_proc_conn_chg (status, handle, 0, 0, 0, 0); + else + btm_sco_connected (status, NULL, handle, &esco_data); + } + break; +#endif + +/* This is commented out until an upper layer cares about returning event +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + case HCI_ENHANCED_FLUSH: + break; +#endif +*/ + default: + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) + btm_vsc_complete (&status, opcode, 1, (tBTM_CMPL_CB *)p_vsc_status_cback); + break; + } + + } + else + { + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) + btm_vsc_complete (&status, opcode, 1, (tBTM_CMPL_CB *)p_vsc_status_cback); + } +#if BTM_PWR_MGR_INCLUDED == TRUE + } +#endif +} + +/******************************************************************************* +** +** Function btu_hcif_command_status_evt +** +** Description Process event HCI_COMMAND_STATUS_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_command_status_evt (UINT8 controller_id, UINT8 *p, UINT16 evt_len) +{ + tHCI_CMD_CB * p_hci_cmd_cb = &(btu_cb.hci_cmd_cb[controller_id]); + UINT8 status; + UINT16 opcode; + BT_HDR *p_cmd = NULL; + UINT16 cmd_opcode; + UINT8 *p_data = NULL; + void *p_vsc_status_cback = NULL; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT8 (p_hci_cmd_cb->cmd_window, p); + +#if (defined(HCI_MAX_SIMUL_CMDS) && (HCI_MAX_SIMUL_CMDS > 0)) + if (p_hci_cmd_cb->cmd_window > HCI_MAX_SIMUL_CMDS) + p_hci_cmd_cb->cmd_window = HCI_MAX_SIMUL_CMDS; +#endif + + STREAM_TO_UINT16 (opcode, p); + + /* only do this for certain commands */ + if ((opcode != HCI_RESET) && (opcode != HCI_HOST_NUM_PACKETS_DONE) && + (opcode != HCI_COMMAND_NONE)) + { + /* dequeue stored command */ + if ((p_cmd = (BT_HDR *) GKI_dequeue (&(p_hci_cmd_cb->cmd_cmpl_q))) != NULL) + { + /* verify event opcode matches command opcode */ + p_data = (UINT8 *)(p_cmd + 1) + p_cmd->offset; + STREAM_TO_UINT16 (cmd_opcode, p_data); + + if (cmd_opcode != opcode) + { + p_data = NULL; + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, + "Event mismatch opcode=%X cmd opcode=%X", opcode, cmd_opcode); + } + /* If command was a VSC, then extract command_status callback */ + else if ((cmd_opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC) + { + p_vsc_status_cback = *((void **)(p_cmd + 1)); + } + } + + /* if more commands in queue restart timer */ + if (BTU_CMD_CMPL_TIMEOUT > 0) + { + if (!GKI_queue_is_empty (&(p_hci_cmd_cb->cmd_cmpl_q))) + { +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + p_hci_cmd_cb->checked_hcisu = FALSE; +#endif + btu_start_timer (&(p_hci_cmd_cb->cmd_cmpl_timer), + (UINT16)(BTU_TTYPE_BTU_CMD_CMPL + controller_id), + BTU_CMD_CMPL_TIMEOUT); + } + else + { + btu_stop_timer (&(p_hci_cmd_cb->cmd_cmpl_timer)); + } + } + } + + /* handle command */ + btu_hcif_hdl_command_status (opcode, status, p_data, p_vsc_status_cback, controller_id); + + /* free stored command */ + if (p_cmd != NULL) + { + GKI_freebuf (p_cmd); + } + + /* See if we can forward any more commands */ + btu_hcif_send_cmd (controller_id, NULL); +} + +/******************************************************************************* +** +** Function btu_hcif_cmd_timeout +** +** Description Handle a command timeout +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_cmd_timeout (UINT8 controller_id) +{ + tHCI_CMD_CB * p_hci_cmd_cb = &(btu_cb.hci_cmd_cb[controller_id]); + BT_HDR *p_cmd; + UINT8 *p; + void *p_cplt_cback = NULL; + UINT16 opcode; +// btla-specific ++ + UINT16 event; +// btla-specific -- + +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + if (!(p_hci_cmd_cb->checked_hcisu)) + { + BT_TRACE_1 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, "BTU HCI(id=%d) command timeout - double check HCISU", controller_id); + + /* trigger HCISU to read any pending data in transport buffer */ + GKI_send_event(HCISU_TASK, HCISU_EVT_MASK); + + btu_start_timer (&(p_hci_cmd_cb->cmd_cmpl_timer), + (UINT16)(BTU_TTYPE_BTU_CMD_CMPL + controller_id), + 2); /* start short timer, if timer is set to 1 then it could expire before HCISU checks. */ + + p_hci_cmd_cb->checked_hcisu = TRUE; + + return; + } +#endif + + /* set the controller cmd window to 1, as if we received a response, so + ** the flow of commands from the stack doesn't hang */ + p_hci_cmd_cb->cmd_window = 1; + + /* get queued command */ + if ((p_cmd = (BT_HDR *) GKI_dequeue (&(p_hci_cmd_cb->cmd_cmpl_q))) == NULL) + { + BT_TRACE_0 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, "Cmd timeout; no cmd in queue"); + return; + } + + /* if more commands in queue restart timer */ + if (BTU_CMD_CMPL_TIMEOUT > 0) + { + if (!GKI_queue_is_empty (&(p_hci_cmd_cb->cmd_cmpl_q))) + { +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + p_hci_cmd_cb->checked_hcisu = FALSE; +#endif + btu_start_timer (&(p_hci_cmd_cb->cmd_cmpl_timer), + (UINT16)(BTU_TTYPE_BTU_CMD_CMPL + controller_id), + BTU_CMD_CMPL_TIMEOUT); + } + } + + p = (UINT8 *)(p_cmd + 1) + p_cmd->offset; +#if (NFC_INCLUDED == TRUE) + if (controller_id == NFC_CONTROLLER_ID) + { + //TODO call nfc_ncif_cmd_timeout + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, "BTU NCI command timeout - header 0x%02x%02x", p[0], p[1]); + return; + } +#endif + + /* get opcode from stored command */ + STREAM_TO_UINT16 (opcode, p); + +// btla-specific ++ +#if (defined(ANDROID_APP_INCLUDED) && (ANDROID_APP_INCLUDED == TRUE)) + ALOGE("######################################################################"); + ALOGE("#"); + ALOGE("# WARNING : BTU HCI(id=%d) command timeout. opcode=0x%x", controller_id, opcode); + ALOGE("#"); + ALOGE("######################################################################"); +#else + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_WARNING, "BTU HCI(id=%d) command timeout. opcode=0x%x", controller_id, opcode); +#endif +// btla-specific ++ + + /* send stack a fake command complete or command status, but first determine + ** which to send + */ + switch (opcode) + { + case HCI_HOLD_MODE: + case HCI_SNIFF_MODE: + case HCI_EXIT_SNIFF_MODE: + case HCI_PARK_MODE: + case HCI_EXIT_PARK_MODE: + case HCI_INQUIRY: + case HCI_RMT_NAME_REQUEST: + case HCI_QOS_SETUP_COMP_EVT: + case HCI_CREATE_CONNECTION: + case HCI_CHANGE_CONN_LINK_KEY: + case HCI_SWITCH_ROLE: + case HCI_READ_RMT_EXT_FEATURES_COMP_EVT: + case HCI_AUTHENTICATION_REQUESTED: + case HCI_SET_CONN_ENCRYPTION: +#if BTM_SCO_INCLUDED == TRUE + case HCI_SETUP_ESCO_CONNECTION: +#endif + /* fake a command status */ + btu_hcif_hdl_command_status (opcode, HCI_ERR_UNSPECIFIED, p, NULL, controller_id); + break; + + default: + /* If vendor specific restore the callback function */ + if ((opcode & HCI_GRP_VENDOR_SPECIFIC) == HCI_GRP_VENDOR_SPECIFIC +#if BLE_INCLUDED == TRUE + || (opcode == HCI_BLE_RAND ) || + (opcode == HCI_BLE_ENCRYPT) +#endif + ) + { + p_cplt_cback = *((void **)(p_cmd + 1)); +#if 0 + BT_TRACE_2 (TRACE_LAYER_HCI, TRACE_TYPE_DEBUG, + "Restoring VSC callback for opcode=0x%04x, Callback function=0x%07x", + opcode, (UINT32)p_cplt_cback); +#endif + } + + /* fake a command complete; first create a fake event */ +// btla-specific ++ + event = HCI_ERR_UNSPECIFIED; + btu_hcif_hdl_command_complete (opcode, (UINT8 *)&event, 1, p_cplt_cback, controller_id); +// btla-specific -- + break; + } + + /* free stored command */ + GKI_freebuf(p_cmd); + + /* If anyone wants device status notifications, give him one */ + btm_report_device_status (BTM_DEV_STATUS_CMD_TOUT); + + /* See if we can forward any more commands */ + btu_hcif_send_cmd (controller_id, NULL); +} + +/******************************************************************************* +** +** Function btu_hcif_hardware_error_evt +** +** Description Process event HCI_HARDWARE_ERROR_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_hardware_error_evt (UINT8 *p, UINT16 evt_len) +{ + BT_TRACE_1 (TRACE_LAYER_HCI, TRACE_TYPE_ERROR, "Ctlr H/w error event - code:0x%x", *p); + + /* If anyone wants device status notifications, give him one. */ + btm_report_device_status (BTM_DEV_STATUS_DOWN); + + /* Reset the controller */ + if (BTM_IsDeviceUp()) + BTM_DeviceReset (NULL); +} + + +/******************************************************************************* +** +** Function btu_hcif_flush_occured_evt +** +** Description Process event HCI_FLUSH_OCCURED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_flush_occured_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_role_change_evt +** +** Description Process event HCI_ROLE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_role_change_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + BD_ADDR bda; + UINT8 role; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (role, p); + + l2c_link_role_changed (bda, role, status); + btm_acl_role_changed(status, bda, role); +} + + +/******************************************************************************* +** +** Function btu_hcif_num_compl_data_pkts_evt +** +** Description Process event HCI_NUM_COMPL_DATA_PKTS_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_num_compl_data_pkts_evt (UINT8 *p, UINT16 evt_len) +{ + /* Process for L2CAP and SCO */ + l2c_link_process_num_completed_pkts (p); + + /* Send on to SCO */ + /*?? No SCO for now */ +} + +/******************************************************************************* +** +** Function btu_hcif_mode_change_evt +** +** Description Process event HCI_MODE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_mode_change_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT8 current_mode; + UINT16 interval; + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (current_mode, p); + STREAM_TO_UINT16 (interval, p); +#if BTM_PWR_MGR_INCLUDED == TRUE +#if BTM_SCO_WAKE_PARKED_LINK == TRUE + btm_sco_chk_pend_unpark (status, handle); +#endif + btm_pm_proc_mode_change (status, handle, current_mode, interval); +#else + btm_process_mode_change (status, handle, current_mode, interval); +#endif /* BTM_PWR_MGR_INCLUDED == TRUE */ + +#if (HID_DEV_INCLUDED == TRUE) && (HID_DEV_PM_INCLUDED == TRUE) + hidd_pm_proc_mode_change( status, current_mode, interval ) ; +#endif +} + +/******************************************************************************* +** +** Function btu_hcif_ssr_evt +** +** Description Process event HCI_SNIFF_SUB_RATE_EVT +** +** Returns void +** +*******************************************************************************/ + #if (BTM_SSR_INCLUDED == TRUE) +static void btu_hcif_ssr_evt (UINT8 *p, UINT16 evt_len) +{ +#if (BTM_PWR_MGR_INCLUDED == TRUE) + btm_pm_proc_ssr_evt(p, evt_len); +#endif +} + #endif + + +/******************************************************************************* +** +** Function btu_hcif_return_link_keys_evt +** +** Description Process event HCI_RETURN_LINK_KEYS_EVT +** +** Returns void +** +*******************************************************************************/ + +static void btu_hcif_return_link_keys_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 num_keys; + tBTM_RETURN_LINK_KEYS_EVT *result; + + /* get the number of link keys */ + num_keys = *p; + + /* If there are no link keys don't call the call back */ + if (!num_keys) + return; + + /* Take one extra byte at the beginning to specify event */ + result = (tBTM_RETURN_LINK_KEYS_EVT *)(--p); + result->event = BTM_CB_EVT_RETURN_LINK_KEYS; + + /* Call the BTM function to pass the link keys to application */ + btm_return_link_keys_evt (result); +} + + +/******************************************************************************* +** +** Function btu_hcif_pin_code_request_evt +** +** Description Process event HCI_PIN_CODE_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_pin_code_request_evt (UINT8 *p, UINT16 evt_len) +{ + BD_ADDR bda; + + STREAM_TO_BDADDR (bda, p); + + /* Tell L2CAP that there was a PIN code request, */ + /* it may need to stretch timeouts */ + l2c_pin_code_request (bda); + + btm_sec_pin_code_request (bda); +} + + +/******************************************************************************* +** +** Function btu_hcif_link_key_request_evt +** +** Description Process event HCI_LINK_KEY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_key_request_evt (UINT8 *p, UINT16 evt_len) +{ + BD_ADDR bda; + + STREAM_TO_BDADDR (bda, p); + btm_sec_link_key_request (bda); +} + + +/******************************************************************************* +** +** Function btu_hcif_link_key_notification_evt +** +** Description Process event HCI_LINK_KEY_NOTIFICATION_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_key_notification_evt (UINT8 *p, UINT16 evt_len) +{ + BD_ADDR bda; + LINK_KEY key; + UINT8 key_type; + + STREAM_TO_BDADDR (bda, p); + STREAM_TO_ARRAY16 (key, p); + STREAM_TO_UINT8 (key_type, p); + + btm_sec_link_key_notification (bda, key, key_type); +} + + +/******************************************************************************* +** +** Function btu_hcif_loopback_command_evt +** +** Description Process event HCI_LOOPBACK_COMMAND_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_loopback_command_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_data_buf_overflow_evt +** +** Description Process event HCI_DATA_BUF_OVERFLOW_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_data_buf_overflow_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_max_slots_changed_evt +** +** Description Process event HCI_MAX_SLOTS_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_max_slots_changed_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_read_clock_off_comp_evt +** +** Description Process event HCI_READ_CLOCK_OFF_COMP_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_read_clock_off_comp_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT16 clock_offset; + + STREAM_TO_UINT8 (status, p); + + /* If failed to get clock offset just drop the result */ + if (status != HCI_SUCCESS) + return; + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (clock_offset, p); + + handle = HCID_GET_HANDLE (handle); + + btm_process_clk_off_comp_evt (handle, clock_offset); + btm_sec_update_clock_offset (handle, clock_offset); +} + + +/******************************************************************************* +** +** Function btu_hcif_conn_pkt_type_change_evt +** +** Description Process event HCI_CONN_PKT_TYPE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_conn_pkt_type_change_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_qos_violation_evt +** +** Description Process event HCI_QOS_VIOLATION_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_qos_violation_evt (UINT8 *p, UINT16 evt_len) +{ + UINT16 handle; + + STREAM_TO_UINT16 (handle, p); + + handle = HCID_GET_HANDLE (handle); + + + l2c_link_hci_qos_violation (handle); +} + + +/******************************************************************************* +** +** Function btu_hcif_page_scan_mode_change_evt +** +** Description Process event HCI_PAGE_SCAN_MODE_CHANGE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_page_scan_mode_change_evt (UINT8 *p, UINT16 evt_len) +{ +} + + +/******************************************************************************* +** +** Function btu_hcif_page_scan_rep_mode_chng_evt +** +** Description Process event HCI_PAGE_SCAN_REP_MODE_CHNG_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_page_scan_rep_mode_chng_evt (UINT8 *p, UINT16 evt_len) +{ +} + +/********************************************** +** Simple Pairing Events +***********************************************/ + +/******************************************************************************* +** +** Function btu_hcif_host_support_evt +** +** Description Process event HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_host_support_evt (UINT8 *p, UINT16 evt_len) +{ + btm_sec_rmt_host_support_feat_evt(p); +} + +/******************************************************************************* +** +** Function btu_hcif_io_cap_request_evt +** +** Description Process event HCI_IO_CAPABILITY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_io_cap_request_evt (UINT8 *p, UINT16 evt_len) +{ + btm_io_capabilities_req(p); +} + + +/******************************************************************************* +** +** Function btu_hcif_io_cap_response_evt +** +** Description Process event HCI_IO_CAPABILITY_RESPONSE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_io_cap_response_evt (UINT8 *p, UINT16 evt_len) +{ + btm_io_capabilities_rsp(p); +} + + +/******************************************************************************* +** +** Function btu_hcif_user_conf_request_evt +** +** Description Process event HCI_USER_CONFIRMATION_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_conf_request_evt (UINT8 *p, UINT16 evt_len) +{ + btm_proc_sp_req_evt(BTM_SP_CFM_REQ_EVT, p); +} + + +/******************************************************************************* +** +** Function btu_hcif_user_passkey_request_evt +** +** Description Process event HCI_USER_PASSKEY_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_passkey_request_evt (UINT8 *p, UINT16 evt_len) +{ + btm_proc_sp_req_evt(BTM_SP_KEY_REQ_EVT, p); +} + +/******************************************************************************* +** +** Function btu_hcif_user_passkey_notif_evt +** +** Description Process event HCI_USER_PASSKEY_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_user_passkey_notif_evt (UINT8 *p, UINT16 evt_len) +{ + btm_proc_sp_req_evt(BTM_SP_KEY_NOTIF_EVT, p); +} + +/******************************************************************************* +** +** Function btu_hcif_keypress_notif_evt +** +** Description Process event HCI_KEYPRESS_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_keypress_notif_evt (UINT8 *p, UINT16 evt_len) +{ + btm_keypress_notif_evt(p); +} + +/******************************************************************************* +** +** Function btu_hcif_link_super_tout_evt +** +** Description Process event HCI_LINK_SUPER_TOUT_CHANGED_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_link_super_tout_evt (UINT8 *p, UINT16 evt_len) +{ + UINT16 handle, timeout; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (timeout, p); + + btm_proc_lsto_evt(handle, timeout); +} + +/******************************************************************************* +** +** Function btu_hcif_rem_oob_request_evt +** +** Description Process event HCI_REMOTE_OOB_DATA_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ + #if BTM_OOB_INCLUDED == TRUE +static void btu_hcif_rem_oob_request_evt (UINT8 *p, UINT16 evt_len) +{ + btm_rem_oob_req(p); +} + #endif + +/******************************************************************************* +** +** Function btu_hcif_simple_pair_complete_evt +** +** Description Process event HCI_SIMPLE_PAIRING_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +static void btu_hcif_simple_pair_complete_evt (UINT8 *p, UINT16 evt_len) +{ + btm_simple_pair_complete(p); +} +/******************************************************************************* +** +** Function btu_hcif_flush_cmd_queue +** +** Description Flush the HCI command complete queue and transmit queue when +** needed. +** +** Returns void +** +*******************************************************************************/ +void btu_hcif_flush_cmd_queue(void) +{ + BT_HDR *p_cmd; + + btu_cb.hci_cmd_cb[0].cmd_window = 0; + while ((p_cmd = (BT_HDR *) GKI_dequeue (&btu_cb.hci_cmd_cb[0].cmd_cmpl_q)) != NULL) + { + GKI_freebuf (p_cmd); + } + while ((p_cmd = (BT_HDR *) GKI_dequeue (&btu_cb.hci_cmd_cb[0].cmd_xmit_q)) != NULL) + { + GKI_freebuf (p_cmd); + } +} + +/******************************************************************************* +** +** Function btu_hcif_enhanced_flush_complete_evt +** +** Description Process event HCI_ENHANCED_FLUSH_COMPLETE_EVT +** +** Returns void +** +*******************************************************************************/ +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +static void btu_hcif_enhanced_flush_complete_evt (UINT8 *p, UINT16 evt_len) +{ +/* This is empty until an upper layer cares about returning event */ +} +#endif +/********************************************** +** End of Simple Pairing Events +***********************************************/ + + +/********************************************** +** BLE Events +***********************************************/ +#if (defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE) +static void btu_hcif_encyption_key_refresh_cmpl_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + btm_sec_encrypt_change (handle, status, 1); +} + +static void btu_ble_process_adv_pkt (UINT8 *p, UINT16 evt_len) +{ + BT_TRACE_0 (TRACE_LAYER_HCI, TRACE_TYPE_EVENT, "btu_ble_process_adv_pkt"); + + btm_ble_process_adv_pkt(p); +} + + +static void btu_ble_ll_conn_complete_evt ( UINT8 *p, UINT16 evt_len) +{ + UINT8 role, status, bda_type; + UINT16 handle; + BD_ADDR bda; + UINT16 conn_interval, conn_latency, conn_timeout; + UINT16 combined_mode; + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (role, p); + STREAM_TO_UINT8 (bda_type, p); + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + + handle = HCID_GET_HANDLE (handle); + + if (status == 0) + { + btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role); + + l2cble_conn_comp (handle, role, bda, bda_type, conn_interval, + conn_latency, conn_timeout); + } + else + { + /* If we are LE connectable, check if we need to start advertising again */ + if (btm_cb.ble_ctr_cb.inq_var.connectable_mode != BTM_BLE_NON_CONNECTABLE) + { + tACL_CONN *pa = &btm_cb.acl_db[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, pa++) + { + /* If any other LE link is up, we are still not connectable */ + if (pa->in_use && pa->is_le_link) + return; + } + combined_mode = (btm_cb.ble_ctr_cb.inq_var.connectable_mode | btm_cb.btm_inq_vars.connectable_mode); + btm_ble_set_connectability ( combined_mode ); + } + } +} + +static void btu_ble_ll_conn_param_upd_evt (UINT8 *p, UINT16 evt_len) +{ +/* This is empty until an upper layer cares about returning event */ +} + +static void btu_ble_read_remote_feat_evt (UINT8 *p, UINT16 evt_len) +{ + btm_ble_read_remote_features_complete(p); +} + +static void btu_ble_proc_ltk_req (UINT8 *p, UINT16 evt_len) +{ + UINT16 ediv, handle; + UINT8 *pp; + + STREAM_TO_UINT16(handle, p); + pp = p + 8; + STREAM_TO_UINT16(ediv, pp); +#if SMP_INCLUDED == TRUE + btm_ble_ltk_request(handle, p, ediv); +#endif + /* This is empty until an upper layer cares about returning event */ +} +/********************************************** +** End of BLE Events Handler +***********************************************/ +#endif /* BLE_INCLUDED */ + diff --git a/stack/btu/btu_init.c b/stack/btu/btu_init.c new file mode 100644 index 0000000..fe52361 --- /dev/null +++ b/stack/btu/btu_init.c @@ -0,0 +1,157 @@ +/****************************************************************************** + * + * Copyright (C) 2000-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 module contains the routines that load and shutdown the core stack + * components. + * + ******************************************************************************/ + +#include "bt_target.h" +#include +#include "dyn_mem.h" + +#include "btu.h" +#include "btm_int.h" +#include "sdpint.h" +#include "l2c_int.h" + +#if (BLE_INCLUDED == TRUE) +#include "gatt_api.h" +#include "gatt_int.h" +#if SMP_INCLUDED == TRUE +#include "smp_int.h" +#endif +#endif + +extern void PLATFORM_DisableHciTransport(UINT8 bDisable); +/***************************************************************************** +** V A R I A B L E S * +******************************************************************************/ +const BD_ADDR BT_BD_ANY = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +/***************************************************************************** +** F U N C T I O N S * +******************************************************************************/ +/***************************************************************************** +** +** Function btu_init_core +** +** Description Initialize control block memory for each core component. +** +** +** Returns void +** +******************************************************************************/ +void btu_init_core(void) +{ + /* Initialize the mandatory core stack components */ + btm_init(); + + l2c_init(); + + sdp_init(); + +#if BLE_INCLUDED == TRUE + gatt_init(); +#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) + SMP_Init(); +#endif + btm_ble_init(); +#endif +} + + +/***************************************************************************** +** +** Function BTE_Init +** +** Description Initializes the BTU control block. +** +** NOTE: Must be called before creating any tasks +** (RPC, BTU, HCIT, APPL, etc.) +** +** Returns void +** +******************************************************************************/ +void BTE_Init(void) +{ + int i = 0; + + memset (&btu_cb, 0, sizeof (tBTU_CB)); + btu_cb.hcit_acl_pkt_size = BTU_DEFAULT_DATA_SIZE + HCI_DATA_PREAMBLE_SIZE; +#if (BLE_INCLUDED == TRUE) + btu_cb.hcit_ble_acl_pkt_size = BTU_DEFAULT_BLE_DATA_SIZE + HCI_DATA_PREAMBLE_SIZE; +#endif + btu_cb.trace_level = HCI_INITIAL_TRACE_LEVEL; + + for ( i = 0; i < BTU_MAX_LOCAL_CTRLS; i++ ) /* include BR/EDR */ + btu_cb.hci_cmd_cb[i].cmd_window = 1; +} + + +/***************************************************************************** +** +** Function BTU_AclPktSize +** +** Description export the ACL packet size. +** +** Returns UINT16 +** +******************************************************************************/ +UINT16 BTU_AclPktSize(void) +{ + return btu_cb.hcit_acl_pkt_size; +} +/***************************************************************************** +** +** Function BTU_BleAclPktSize +** +** Description export the BLE ACL packet size. +** +** Returns UINT16 +** +******************************************************************************/ +UINT16 BTU_BleAclPktSize(void) +{ +#if BLE_INCLUDED == TRUE + return btu_cb.hcit_ble_acl_pkt_size; +#else + return 0; +#endif +} + +/******************************************************************************* +** +** Function btu_uipc_rx_cback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void btu_uipc_rx_cback(BT_HDR *p_msg) +{ + BT_TRACE_3 (TRACE_LAYER_BTM, TRACE_TYPE_DEBUG, "btu_uipc_rx_cback event 0x%x, len %d, offset %d", + p_msg->event, p_msg->len, p_msg->offset); + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); + +} + diff --git a/stack/btu/btu_task.c b/stack/btu/btu_task.c new file mode 100644 index 0000000..48ce489 --- /dev/null +++ b/stack/btu/btu_task.c @@ -0,0 +1,835 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the main Bluetooth Upper Layer processing loop. + * The Broadcom implementations of L2CAP RFCOMM, SDP and the BTIf run as one + * GKI task. This btu_task switches between them. + * + * Note that there will always be an L2CAP, but there may or may not be an + * RFCOMM or SDP. Whether these layers are present or not is determined by + * compile switches. + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "l2c_int.h" +#include "btu.h" +#include "bt_utils.h" + +#include "sdpint.h" + +#if ( defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE ) +#include "port_api.h" +#include "port_ext.h" +#endif + +#include "btm_api.h" +#include "btm_int.h" + +#if (defined(EVAL) && EVAL == TRUE) +#include "btu_eval.h" +#endif + +#if GAP_INCLUDED == TRUE +#include "gap_int.h" +#endif + +#if (defined(OBX_INCLUDED) && OBX_INCLUDED == TRUE) +#include "obx_int.h" + +#if (defined(BIP_INCLUDED) && BIP_INCLUDED == TRUE) +#include "bip_int.h" +#endif /* BIP */ + +#if (BPP_SND_INCLUDED == TRUE || BPP_INCLUDED == TRUE) +#include "bpp_int.h" +#endif /* BPP */ + +#endif /* OBX */ + +#include "bt_trace.h" + +/* BTE application task */ +#if APPL_INCLUDED == TRUE +#include "bte_appl.h" +#endif + +#if (defined(RPC_INCLUDED) && RPC_INCLUDED == TRUE) +#include "rpct_main.h" +#endif + +#if (defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE) +#include "bnep_int.h" +#endif + +#if (defined(PAN_INCLUDED) && PAN_INCLUDED == TRUE) +#include "pan_int.h" +#endif + +#if (defined(SAP_SERVER_INCLUDED) && SAP_SERVER_INCLUDED == TRUE) +#include "sap_int.h" +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE ) +#include "hidd_int.h" +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE ) +#include "hidh_int.h" +#endif + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) +#include "avdt_int.h" +#else +extern void avdt_rcv_sync_info (BT_HDR *p_buf); /* this is for hci_test */ +#endif + +#if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" +#endif + + +#if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE) +#include "bta_sys.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#include "gatt_int.h" +#if (SMP_INCLUDED == TRUE) +#include "smp_int.h" +#endif +#include "btm_ble_int.h" +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +BT_API extern void BTE_InitStack(void); + +#ifdef __cplusplus +} +#endif + +/* Define BTU storage area +*/ +#if BTU_DYNAMIC_MEMORY == FALSE +tBTU_CB btu_cb; +#endif + + +/* Define a function prototype to allow a generic timeout handler */ +typedef void (tUSER_TIMEOUT_FUNC) (TIMER_LIST_ENT *p_tle); + +/******************************************************************************* +** +** Function btu_task +** +** Description This is the main task of the Bluetooth Upper Layers unit. +** It sits in a loop waiting for messages, and dispatches them +** to the appropiate handlers. +** +** Returns should never return +** +*******************************************************************************/ +BTU_API UINT32 btu_task (UINT32 param) +{ + UINT16 event; + BT_HDR *p_msg; + UINT8 i; + UINT16 mask; + BOOLEAN handled; + +#if (defined(HCISU_H4_INCLUDED) && HCISU_H4_INCLUDED == TRUE) + /* wait an event that HCISU is ready */ + GKI_wait(0xFFFF, 0); +#endif + /* Initialize the mandatory core stack control blocks + (BTU, BTM, L2CAP, and SDP) + */ + btu_init_core(); + + /* Initialize any optional stack components */ + BTE_InitStack(); + +#if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE) + bta_sys_init(); +#endif + + /* Initialise platform trace levels at this point as BTE_InitStack() and bta_sys_init() + * reset the control blocks and preset the trace level with XXX_INITIAL_TRACE_LEVEL + */ +#if ( BT_USE_TRACES==TRUE ) + BTE_InitTraceLevels(); +#endif + + /* Send a startup evt message to BTIF_TASK to kickstart the init procedure */ + GKI_send_event(BTIF_TASK, BT_EVT_TRIGGER_STACK_INIT); + + raise_priority_a2dp(TASK_HIGH_BTU); + + /* Wait for, and process, events */ + for (;;) + { + event = GKI_wait (0xFFFF, 0); + + if (event & TASK_MBOX_0_EVT_MASK) + { + /* Process all messages in the queue */ + while ((p_msg = (BT_HDR *) GKI_read_mbox (BTU_HCI_RCV_MBOX)) != NULL) + { + /* Determine the input message type. */ + switch (p_msg->event & BT_EVT_MASK) + { + case BT_EVT_TO_BTU_HCI_ACL: + /* All Acl Data goes to L2CAP */ + l2c_rcv_acl_data (p_msg); + break; + + case BT_EVT_TO_BTU_L2C_SEG_XMIT: + /* L2CAP segment transmit complete */ + l2c_link_segments_xmitted (p_msg); + break; + + case BT_EVT_TO_BTU_HCI_SCO: +#if BTM_SCO_INCLUDED == TRUE + btm_route_sco_data (p_msg); + break; +#endif + + case BT_EVT_TO_BTU_HCI_EVT: + btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); + GKI_freebuf(p_msg); + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + /* If host receives events which it doesn't response to, */ + /* host should start idle timer to enter sleep mode. */ + btu_check_bt_sleep (); +#endif + break; + + case BT_EVT_TO_BTU_HCI_CMD: + btu_hcif_send_cmd ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg); + break; + +#if (defined(OBX_INCLUDED) && OBX_INCLUDED == TRUE) +#if (defined(OBX_SERVER_INCLUDED) && OBX_SERVER_INCLUDED == TRUE) + case BT_EVT_TO_OBX_SR_MSG: + obx_sr_proc_evt((tOBX_PORT_EVT *)(p_msg + 1)); + GKI_freebuf (p_msg); + break; + + case BT_EVT_TO_OBX_SR_L2C_MSG: + obx_sr_proc_l2c_evt((tOBX_L2C_EVT_MSG *)(p_msg + 1)); + GKI_freebuf (p_msg); + break; +#endif + +#if (defined(OBX_CLIENT_INCLUDED) && OBX_CLIENT_INCLUDED == TRUE) + case BT_EVT_TO_OBX_CL_MSG: + obx_cl_proc_evt((tOBX_PORT_EVT *)(p_msg + 1)); + GKI_freebuf (p_msg); + break; + + case BT_EVT_TO_OBX_CL_L2C_MSG: + obx_cl_proc_l2c_evt((tOBX_L2C_EVT_MSG *)(p_msg + 1)); + GKI_freebuf (p_msg); + break; +#endif + +#if (defined(BIP_INCLUDED) && BIP_INCLUDED == TRUE) + case BT_EVT_TO_BIP_CMDS : + bip_proc_btu_event(p_msg); + GKI_freebuf (p_msg); + break; +#endif /* BIP */ +#if (BPP_SND_INCLUDED == TRUE || BPP_INCLUDED == TRUE) + case BT_EVT_TO_BPP_PR_CMDS: + bpp_pr_proc_event(p_msg); + GKI_freebuf (p_msg); + break; + case BT_EVT_TO_BPP_SND_CMDS: + bpp_snd_proc_event(p_msg); + GKI_freebuf (p_msg); + break; + +#endif /* BPP */ + +#endif /* OBX */ + +#if (defined(SAP_SERVER_INCLUDED) && SAP_SERVER_INCLUDED == TRUE) + case BT_EVT_TO_BTU_SAP : + sap_proc_btu_event(p_msg); + GKI_freebuf (p_msg); + break; +#endif /* SAP */ +#if (defined(GAP_CONN_INCLUDED) && GAP_CONN_INCLUDED == TRUE && GAP_CONN_POST_EVT_INCLUDED == TRUE) + case BT_EVT_TO_GAP_MSG : + gap_proc_btu_event(p_msg); + GKI_freebuf (p_msg); + break; +#endif + case BT_EVT_TO_START_TIMER : + /* Start free running 1 second timer for list management */ + GKI_start_timer (TIMER_0, GKI_SECS_TO_TICKS (1), TRUE); + GKI_freebuf (p_msg); + break; + +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) + case BT_EVT_TO_START_QUICK_TIMER : + GKI_start_timer (TIMER_2, QUICK_TIMER_TICKS, TRUE); + GKI_freebuf (p_msg); + break; +#endif + + default: + i = 0; + mask = (UINT16) (p_msg->event & BT_EVT_MASK); + handled = FALSE; + + for (; !handled && i < BTU_MAX_REG_EVENT; i++) + { + if (btu_cb.event_reg[i].event_cb == NULL) + continue; + + if (mask == btu_cb.event_reg[i].event_range) + { + if (btu_cb.event_reg[i].event_cb) + { + btu_cb.event_reg[i].event_cb(p_msg); + handled = TRUE; + } + } + } + + if (handled == FALSE) + GKI_freebuf (p_msg); + + break; + } + } + } + + + if (event & TIMER_0_EVT_MASK) + { + TIMER_LIST_ENT *p_tle; + + GKI_update_timer_list (&btu_cb.timer_queue, 1); + + while ((btu_cb.timer_queue.p_first) && (!btu_cb.timer_queue.p_first->ticks)) + { + p_tle = btu_cb.timer_queue.p_first; + GKI_remove_from_timer_list (&btu_cb.timer_queue, p_tle); + + switch (p_tle->event) + { + case BTU_TTYPE_BTM_DEV_CTL: + btm_dev_timeout(p_tle); + break; + + case BTU_TTYPE_BTM_ACL: + btm_acl_timeout(p_tle); + break; + + case BTU_TTYPE_L2CAP_LINK: + case BTU_TTYPE_L2CAP_CHNL: + case BTU_TTYPE_L2CAP_HOLD: + case BTU_TTYPE_L2CAP_INFO: + case BTU_TTYPE_L2CAP_FCR_ACK: + + l2c_process_timeout (p_tle); + break; + + case BTU_TTYPE_SDP: + sdp_conn_timeout ((tCONN_CB *)p_tle->param); + break; + + case BTU_TTYPE_BTM_RMT_NAME: + btm_inq_rmt_name_failed(); + break; + +#if (defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE) + case BTU_TTYPE_RFCOMM_MFC: + case BTU_TTYPE_RFCOMM_PORT: + rfcomm_process_timeout (p_tle); + break; + +#endif /* If defined(RFCOMM_INCLUDED) && RFCOMM_INCLUDED == TRUE */ + +#if ((defined(BNEP_INCLUDED) && BNEP_INCLUDED == TRUE)) + case BTU_TTYPE_BNEP: + bnep_process_timeout(p_tle); + break; +#endif + + +#if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) + case BTU_TTYPE_AVDT_CCB_RET: + case BTU_TTYPE_AVDT_CCB_RSP: + case BTU_TTYPE_AVDT_CCB_IDLE: + case BTU_TTYPE_AVDT_SCB_TC: + avdt_process_timeout(p_tle); + break; +#endif + +#if (defined(OBX_INCLUDED) && OBX_INCLUDED == TRUE) +#if (defined(OBX_CLIENT_INCLUDED) && OBX_CLIENT_INCLUDED == TRUE) + case BTU_TTYPE_OBX_CLIENT_TO: + obx_cl_timeout(p_tle); + break; +#endif +#if (defined(OBX_SERVER_INCLUDED) && OBX_SERVER_INCLUDED == TRUE) + case BTU_TTYPE_OBX_SERVER_TO: + obx_sr_timeout(p_tle); + break; + + case BTU_TTYPE_OBX_SVR_SESS_TO: + obx_sr_sess_timeout(p_tle); + break; +#endif +#endif + +#if (defined(SAP_SERVER_INCLUDED) && SAP_SERVER_INCLUDED == TRUE) + case BTU_TTYPE_SAP_TO: + sap_process_timeout(p_tle); + break; +#endif + + case BTU_TTYPE_BTU_CMD_CMPL: + btu_hcif_cmd_timeout((UINT8)(p_tle->event - BTU_TTYPE_BTU_CMD_CMPL)); + break; + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + case BTU_TTYPE_HID_HOST_REPAGE_TO : + hidh_proc_repage_timeout(p_tle); + break; +#endif + +#if (defined(BLE_INCLUDED) && BLE_INCLUDED == TRUE) + case BTU_TTYPE_BLE_INQUIRY: + case BTU_TTYPE_BLE_GAP_LIM_DISC: + case BTU_TTYPE_BLE_RANDOM_ADDR: + btm_ble_timeout(p_tle); + break; + + case BTU_TTYPE_BLE_SCAN_PARAM_IDLE: + btm_ble_scan_param_idle(); + break; + + case BTU_TTYPE_ATT_WAIT_FOR_RSP: + gatt_rsp_timeout(p_tle); + break; + + case BTU_TTYPE_ATT_WAIT_FOR_IND_ACK: + gatt_ind_ack_timeout(p_tle); + break; +#if (defined(SMP_INCLUDED) && SMP_INCLUDED == TRUE) + case BTU_TTYPE_SMP_PAIRING_CMD: + smp_rsp_timeout(p_tle); + break; +#endif + +#endif + +#if (MCA_INCLUDED == TRUE) + case BTU_TTYPE_MCA_CCB_RSP: + mca_process_timeout(p_tle); + break; +#endif + case BTU_TTYPE_USER_FUNC: + { + tUSER_TIMEOUT_FUNC *p_uf = (tUSER_TIMEOUT_FUNC *)p_tle->param; + (*p_uf)(p_tle); + } + break; + + default: + i = 0; + handled = FALSE; + + for (; !handled && i < BTU_MAX_REG_TIMER; i++) + { + if (btu_cb.timer_reg[i].timer_cb == NULL) + continue; + if (btu_cb.timer_reg[i].p_tle == p_tle) + { + btu_cb.timer_reg[i].timer_cb(p_tle); + handled = TRUE; + } + } + break; + } + } + + /* if timer list is empty stop periodic GKI timer */ + if (btu_cb.timer_queue.p_first == NULL) + { + GKI_stop_timer(TIMER_0); + } + } + +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) + if (event & TIMER_2_EVT_MASK) + { + btu_process_quick_timer_evt(); + } +#endif + + +#if (RPC_INCLUDED == TRUE) + /* if RPC message queue event */ + if (event & RPCGEN_MSG_EVT) + { + if ((p_msg = (BT_HDR *) GKI_read_mbox(RPCGEN_MSG_MBOX)) != NULL) + RPCT_RpcgenMsg(p_msg); /* handle RPC message queue */ + } +#endif + +#if (defined(BTU_BTA_INCLUDED) && BTU_BTA_INCLUDED == TRUE) + if (event & TASK_MBOX_2_EVT_MASK) + { + while ((p_msg = (BT_HDR *) GKI_read_mbox(TASK_MBOX_2)) != NULL) + { + bta_sys_event(p_msg); + } + } + + if (event & TIMER_1_EVT_MASK) + { + bta_sys_timer_update(); + } +#endif + + if (event & EVENT_MASK(APPL_EVT_7)) + break; + } + + return(0); +} + +/******************************************************************************* +** +** Function btu_start_timer +** +** Description Start a timer for the specified amount of time. +** NOTE: The timeout resolution is in SECONDS! (Even +** though the timer structure field is ticks) +** +** Returns void +** +*******************************************************************************/ +void btu_start_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout) +{ + BT_HDR *p_msg; + /* if timer list is currently empty, start periodic GKI timer */ + if (btu_cb.timer_queue.p_first == NULL) + { + /* if timer starts on other than BTU task */ + if (GKI_get_taskid() != BTU_TASK) + { + /* post event to start timer in BTU task */ + if ((p_msg = (BT_HDR *)GKI_getbuf(BT_HDR_SIZE)) != NULL) + { + p_msg->event = BT_EVT_TO_START_TIMER; + GKI_send_msg (BTU_TASK, TASK_MBOX_0, p_msg); + } + } + else + { + /* Start free running 1 second timer for list management */ + GKI_start_timer (TIMER_0, GKI_SECS_TO_TICKS (1), TRUE); + } + } + + GKI_remove_from_timer_list (&btu_cb.timer_queue, p_tle); + + p_tle->event = type; + p_tle->ticks = timeout; /* Save the number of seconds for the timer */ + + GKI_add_to_timer_list (&btu_cb.timer_queue, p_tle); +} + +/******************************************************************************* +** +** Function btu_remaining_time +** +** Description Return amount of time to expire +** +** Returns time in second +** +*******************************************************************************/ +UINT32 btu_remaining_time (TIMER_LIST_ENT *p_tle) +{ + return(GKI_get_remaining_ticks (&btu_cb.timer_queue, p_tle)); +} + +/******************************************************************************* +** +** Function btu_stop_timer +** +** Description Stop a timer. +** +** Returns void +** +*******************************************************************************/ +void btu_stop_timer (TIMER_LIST_ENT *p_tle) +{ + GKI_remove_from_timer_list (&btu_cb.timer_queue, p_tle); + + /* if timer list is empty stop periodic GKI timer */ + if (btu_cb.timer_queue.p_first == NULL) + { + GKI_stop_timer(TIMER_0); + } + +} + +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) +/******************************************************************************* +** +** Function btu_start_quick_timer +** +** Description Start a timer for the specified amount of time. +** NOTE: The timeout resolution depends on including modules. +** QUICK_TIMER_TICKS_PER_SEC should be used to convert from +** time to ticks. +** +** +** Returns void +** +*******************************************************************************/ +void btu_start_quick_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout) +{ + BT_HDR *p_msg; + + /* if timer list is currently empty, start periodic GKI timer */ + if (btu_cb.quick_timer_queue.p_first == NULL) + { + /* script test calls stack API without posting event */ + if (GKI_get_taskid() != BTU_TASK) + { + /* post event to start timer in BTU task */ + if ((p_msg = (BT_HDR *)GKI_getbuf(BT_HDR_SIZE)) != NULL) + { + p_msg->event = BT_EVT_TO_START_QUICK_TIMER; + GKI_send_msg (BTU_TASK, TASK_MBOX_0, p_msg); + } + } + else + GKI_start_timer(TIMER_2, QUICK_TIMER_TICKS, TRUE); + } + + GKI_remove_from_timer_list (&btu_cb.quick_timer_queue, p_tle); + + p_tle->event = type; + p_tle->ticks = timeout; /* Save the number of ticks for the timer */ + + GKI_add_to_timer_list (&btu_cb.quick_timer_queue, p_tle); +} + + +/******************************************************************************* +** +** Function btu_stop_quick_timer +** +** Description Stop a timer. +** +** Returns void +** +*******************************************************************************/ +void btu_stop_quick_timer (TIMER_LIST_ENT *p_tle) +{ + GKI_remove_from_timer_list (&btu_cb.quick_timer_queue, p_tle); + + /* if timer list is empty stop periodic GKI timer */ + if (btu_cb.quick_timer_queue.p_first == NULL) + { + GKI_stop_timer(TIMER_2); + } +} + +/******************************************************************************* +** +** Function btu_process_quick_timer_evt +** +** Description Process quick timer event +** +** Returns void +** +*******************************************************************************/ +void btu_process_quick_timer_evt(void) +{ + process_quick_timer_evt(&btu_cb.quick_timer_queue); + + /* if timer list is empty stop periodic GKI timer */ + if (btu_cb.quick_timer_queue.p_first == NULL) + { + GKI_stop_timer(TIMER_2); + } +} + +/******************************************************************************* +** +** Function process_quick_timer_evt +** +** Description Process quick timer event +** +** Returns void +** +*******************************************************************************/ +void process_quick_timer_evt(TIMER_LIST_Q *p_tlq) +{ + TIMER_LIST_ENT *p_tle; + + GKI_update_timer_list (p_tlq, 1); + + while ((p_tlq->p_first) && (!p_tlq->p_first->ticks)) + { + p_tle = p_tlq->p_first; + GKI_remove_from_timer_list (p_tlq, p_tle); + + switch (p_tle->event) + { + case BTU_TTYPE_L2CAP_CHNL: /* monitor or retransmission timer */ + case BTU_TTYPE_L2CAP_FCR_ACK: /* ack timer */ + l2c_process_timeout (p_tle); + break; + + default: + break; + } + } +} +#endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */ + + + +void btu_register_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout, tBTU_TIMER_CALLBACK timer_cb) +{ + UINT8 i = 0; + INT8 first = -1; + for (; i < BTU_MAX_REG_TIMER; i++) + { + if (btu_cb.timer_reg[i].p_tle == NULL && first < 0) + first = i; + if (btu_cb.timer_reg[i].p_tle == p_tle) + { + btu_cb.timer_reg[i].timer_cb = timer_cb; + btu_start_timer(p_tle, type, timeout); + first = -1; + break; + } + } + + if (first >= 0 && first < BTU_MAX_REG_TIMER) + { + btu_cb.timer_reg[first].timer_cb = timer_cb; + btu_cb.timer_reg[first].p_tle = p_tle; + btu_start_timer(p_tle, type, timeout); + } + +} + + +void btu_deregister_timer(TIMER_LIST_ENT *p_tle) +{ + UINT8 i = 0; + + for (; i < BTU_MAX_REG_TIMER; i++) + { + if (btu_cb.timer_reg[i].p_tle == p_tle) + { + btu_stop_timer(p_tle); + btu_cb.timer_reg[i].timer_cb = NULL; + btu_cb.timer_reg[i].p_tle = NULL; + break; + } + } +} + +void btu_register_event_range (UINT16 start, tBTU_EVENT_CALLBACK event_cb) +{ + UINT8 i = 0; + INT8 first = -1; + + for (; i < BTU_MAX_REG_EVENT; i++) + { + if (btu_cb.event_reg[i].event_cb == NULL && first < 0) + first = i; + + if (btu_cb.event_reg[i].event_range == start) + { + btu_cb.event_reg[i].event_cb = event_cb; + + if (!event_cb) + btu_cb.event_reg[i].event_range = 0; + + first = -1; + } + } + + /* if not deregistering && an empty index was found in range, register */ + if (event_cb && first >= 0 && first < BTU_MAX_REG_EVENT) + { + btu_cb.event_reg[first].event_range = start; + btu_cb.event_reg[first].event_cb = event_cb; + } +} + + +void btu_deregister_event_range (UINT16 range) +{ + btu_register_event_range(range, NULL); +} + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btu_check_bt_sleep +** +** Description This function is called to check if controller can go to sleep. +** +** Returns void +** +*******************************************************************************/ +void btu_check_bt_sleep (void) +{ + if ((btu_cb.hci_cmd_cb[LOCAL_BR_EDR_CONTROLLER_ID].cmd_cmpl_q.count == 0) + &&(btu_cb.hci_cmd_cb[LOCAL_BR_EDR_CONTROLLER_ID].cmd_xmit_q.count == 0)) + { + if (l2cb.controller_xmit_window == l2cb.num_lm_acl_bufs) + { + /* enable dev to sleep in the cmd cplt and cmd status only and num cplt packet */ + HCI_LP_ALLOW_BT_DEVICE_SLEEP(); + } + } +} +#endif diff --git a/stack/gatt/att_protocol.c b/stack/gatt/att_protocol.c new file mode 100644 index 0000000..a114a33 --- /dev/null +++ b/stack/gatt/att_protocol.c @@ -0,0 +1,630 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains ATT protocol functions + * + ******************************************************************************/ + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include "gatt_int.h" +#include "l2c_api.h" + +#define GATT_HDR_FIND_TYPE_VALUE_LEN 21 +#define GATT_OP_CODE_SIZE 1 +/********************************************************************** +** ATT protocl message building utility * +***********************************************************************/ +/******************************************************************************* +** +** Function attp_build_mtu_exec_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, rx_mtu); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */ + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_exec_write_cmd +** +** Description Build a execute write request or response. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + 10 + L2CAP_MIN_OFFSET))) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE; + + UINT8_TO_STREAM (p, op_code); + + if (op_code == GATT_REQ_EXEC_WRITE) + { + flag &= GATT_PREP_WRITE_EXEC; + UINT8_TO_STREAM (p, flag); + p_buf->len += 1; + } + + } + + return p_buf; +} + +/******************************************************************************* +** +** Function attp_build_err_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, GATT_RSP_ERROR); + UINT8_TO_STREAM (p, cmd_code); + UINT16_TO_STREAM(p, err_handle); + UINT8_TO_STREAM (p, reason); + + p_buf->offset = L2CAP_MIN_OFFSET; + /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code + 1B status */ + p_buf->len = GATT_HDR_SIZE + 1 + 1; + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_browse_cmd +** +** Description Build a read information request or read by type request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 8 + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + /* Describe the built message location and size */ + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE + 4; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, s_hdl); + UINT16_TO_STREAM (p, e_hdl); + p_buf->len += gatt_build_uuid_to_stream(&p, uuid); + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_handles_cmd +** +** Description Build a read by type and value request. +** +** Returns pointer to the command buffer. +** +*******************************************************************************/ +BT_HDR *attp_build_read_handles_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + UINT16 len = p_value_type->value_len; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 5; /* opcode + s_handle + e_handle */ + + UINT8_TO_STREAM (p, GATT_REQ_FIND_TYPE_VALUE); + UINT16_TO_STREAM (p, p_value_type->s_handle); + UINT16_TO_STREAM (p, p_value_type->e_handle); + + p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid); + + if (p_value_type->value_len + p_buf->len > payload_size ) + len = payload_size - p_buf->len; + + memcpy (p, p_value_type->value, len); + p_buf->len += len; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_multi_cmd +** +** Description Build a read multiple request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, i = 0; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + UINT8_TO_STREAM (p, GATT_REQ_READ_MULTI); + + for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) + { + UINT16_TO_STREAM (p, *(p_handle + i)); + p_buf->len += 2; + } + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_handle_cmd +** +** Description Build a read /read blob request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + + if (op_code == GATT_REQ_READ_BLOB) + { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_opcode_cmd +** +** Description Build a request/response with opcode only. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_opcode_cmd(UINT8 op_code) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_value_cmd +** +** Description Build a attribute value request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, + UINT16 offset, UINT16 len, UINT8 *p_data) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, *pp, pair_len, *p_pair_len; + + if ((p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) + { + p = pp =(UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + if (op_code == GATT_RSP_READ_BY_TYPE) + { + p_pair_len = p; + pair_len = len + 2; + UINT8_TO_STREAM (p, pair_len); + p_buf->len += 1; + } + if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ) + { + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + } + + if (op_code == GATT_REQ_PREPARE_WRITE ||op_code == GATT_RSP_PREPARE_WRITE ) + { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + if (len > 0 && p_data != NULL) + { + /* ensure data not exceed MTU size */ + if (payload_size - p_buf->len < len) + { + len = payload_size - p_buf->len; + /* update handle value pair length */ + if (op_code == GATT_RSP_READ_BY_TYPE) + *p_pair_len = (len + 2); + + GATT_TRACE_WARNING1("attribute value too long, to be truncated to %d", len); + } + + ARRAY_TO_STREAM (p, p_data, len); + p_buf->len += len; + } + } + return p_buf; +} + +/******************************************************************************* +** +** Function attp_send_msg_to_L2CAP +** +** Description Send message to L2CAP. +** +*******************************************************************************/ +BOOLEAN attp_send_msg_to_L2CAP(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) +{ + UINT16 l2cap_ret; + + + if (p_tcb->att_lcid == L2CAP_ATT_CID) + l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); + else + l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); + + if (l2cap_ret == L2CAP_DW_FAILED) + { + GATT_TRACE_ERROR1("ATT failed to pass msg:0x%0x to L2CAP", + *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); + GKI_freebuf(p_toL2CAP); + return FALSE; + } + else + { + return TRUE; + } +} + +/******************************************************************************* +** +** Function attp_build_sr_msg +** +** Description Build ATT Server PDUs. +** +*******************************************************************************/ +BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg) +{ + BT_HDR *p_cmd = NULL; + UINT16 offset = 0; + + switch (op_code) + { + case GATT_RSP_READ_BLOB: + GATT_TRACE_EVENT2 ("ATT_RSP_READ_BLOB: len = %d offset = %d", p_msg->attr_value.len, p_msg->attr_value.offset); + offset = p_msg->attr_value.offset; + + case GATT_RSP_PREPARE_WRITE: + if (offset == 0) + offset = p_msg->attr_value.offset; + + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ: + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + p_cmd = attp_build_value_cmd(p_tcb->payload_size, + op_code, + p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + break; + + case GATT_RSP_WRITE: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_RSP_ERROR: + p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason); + break; + + case GATT_RSP_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, 0); + break; + + case GATT_RSP_MTU: + p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu); + break; + + default: + GATT_TRACE_DEBUG1("attp_build_sr_msg: unknown op code = %d", op_code); + break; + } + + if (!p_cmd) + GATT_TRACE_ERROR0("No resources"); + + return p_cmd; +} + +/******************************************************************************* +** +** Function attp_send_sr_msg +** +** Description This function sends the server response or indication message +** to client. +** +** Parameter p_tcb: pointer to the connecton control block. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_NO_RESOURCES; + + if (p_tcb != NULL) + { + if (p_msg != NULL) + { + p_msg->offset = L2CAP_MIN_OFFSET; + + if (attp_send_msg_to_L2CAP (p_tcb, p_msg)) + cmd_sent = GATT_SUCCESS; + else + cmd_sent = GATT_INTERNAL_ERROR; + } + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function attp_cl_send_cmd +** +** Description Send a ATT command or enqueue it. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +UINT8 attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd) +{ + UINT8 att_ret = GATT_SUCCESS; + + if (p_tcb != NULL) + { + cmd_code &= ~GATT_AUTH_SIGN_MASK; + + if (p_tcb->pending_cl_req == p_tcb->next_slot_inq || + cmd_code == GATT_HANDLE_VALUE_CONF) + { + /* no penindg request or value confirmation */ + if (attp_send_msg_to_L2CAP(p_tcb, p_cmd)) + { + /* do not enq cmd if handle value confirmation or set request */ + if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) + { + gatt_start_rsp_timer (p_tcb); + gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL); + } + } + else + att_ret = GATT_INTERNAL_ERROR; + } + else + gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd); + } + else + att_ret = GATT_ILLEGAL_PARAMETER; + + return att_ret; +} +/******************************************************************************* +** +** Function attp_send_cl_msg +** +** Description This function sends the client request or confirmation message +** to server. +** +** Parameter p_tcb: pointer to the connectino control block. +** clcb_idx: clcb index +** op_code: message op code. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + BT_HDR *p_cmd = NULL; + UINT16 offset = 0, handle; + + if (p_tcb != NULL) + { + switch (op_code) + { + case GATT_REQ_MTU: + if (p_msg->mtu <= GATT_MAX_MTU_SIZE) + { + p_tcb->payload_size = p_msg->mtu; + p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu); + } + else + status = GATT_ILLEGAL_PARAMETER; + break; + + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_READ_BY_GRP_TYPE: + if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) && + GATT_HANDLE_IS_VALID (p_msg->browse.e_handle) && + p_msg->browse.s_handle <= p_msg->browse.e_handle) + { + p_cmd = attp_build_browse_cmd(op_code, + p_msg->browse.s_handle, + p_msg->browse.e_handle, + p_msg->browse.uuid); + } + else + status = GATT_ILLEGAL_PARAMETER; + break; + + case GATT_REQ_READ_BLOB: + offset = p_msg->read_blob.offset; + /* fall through */ + case GATT_REQ_READ: + handle = (op_code == GATT_REQ_READ) ? p_msg->handle: p_msg->read_blob.handle; + /* handle checking */ + if (GATT_HANDLE_IS_VALID (handle)) + { + p_cmd = attp_build_handle_cmd(op_code, handle, offset); + } + else + status = GATT_ILLEGAL_PARAMETER; + break; + + case GATT_HANDLE_VALUE_CONF: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_REQ_PREPARE_WRITE: + offset = p_msg->attr_value.offset; + /* fall through */ + case GATT_REQ_WRITE: + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) + { + p_cmd = attp_build_value_cmd (p_tcb->payload_size, + op_code, p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + } + else + status = GATT_ILLEGAL_PARAMETER; + break; + + case GATT_REQ_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write); + break; + + case GATT_REQ_FIND_TYPE_VALUE: + p_cmd = attp_build_read_handles_cmd(p_tcb->payload_size, &p_msg->find_type_value); + break; + + case GATT_REQ_READ_MULTI: + p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size, + p_msg->read_multi.num_handles, + p_msg->read_multi.handles); + break; + + default: + break; + } + + if (p_cmd != NULL) + status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd); + + } + else + { + GATT_TRACE_ERROR0("Peer device not connected"); + } + + return status; +} +#endif diff --git a/stack/gatt/gatt_api.c b/stack/gatt/gatt_api.c new file mode 100644 index 0000000..7845f3c --- /dev/null +++ b/stack/gatt/gatt_api.c @@ -0,0 +1,1555 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT interface functions + * + ******************************************************************************/ +#include "bt_target.h" + + +#if defined(BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) + +#include "gki.h" +#include +#include +#include "gatt_api.h" +#include "gatt_int.h" +#include "l2c_api.h" +#include "btm_int.h" + + +/******************************************************************************* +** +** Function GATT_SetTraceLevel +** +** Description This function sets the trace level. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Input Parameters: +** level: The level to set the GATT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 GATT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + gatt_cb.trace_level = new_level; + + return(gatt_cb.trace_level); +} + +/***************************************************************************** +** +** GATT SERVER API +** +******************************************************************************/ +/******************************************************************************* +** +** Function GATTS_AddHandleRange +** +** Description This function add the allocated handles range for the specifed +** application UUID, service UUID and service instance +** +** Parameter p_hndl_range: pointer to allocated handles information +** +** Returns TRUE if handle range is added sucessfully; otherwise FALSE. +** +*******************************************************************************/ + +BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range) +{ + tGATT_HDL_LIST_ELEM *p_buf; + BOOLEAN status= FALSE; + + if ((p_buf = gatt_alloc_hdl_buffer()) != NULL) + { + p_buf->asgn_range = *p_hndl_range; + status = gatt_add_an_item_to_list(&gatt_cb.hdl_list_info, p_buf); + } + return status; +} + + +/******************************************************************************* +** +** Function GATTS_NVRegister +** +** Description Application manager calls this function to register for +** NV save callback function. There can be one and only one +** NV save callback function. +** +** Parameter p_cb_info : callback informaiton +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN GATTS_NVRegister (tGATT_APPL_INFO *p_cb_info) +{ + BOOLEAN status= FALSE; + if (p_cb_info) + { + gatt_cb.cb_info = *p_cb_info; + status = TRUE; + gatt_init_srv_chg(); + } + + return status; +} + +/******************************************************************************* +** +** Function GATTS_CreateService +** +** Description This function is called to reserve a block of handles for a service. +** +** *** It should be called only once per service instance *** +** +** Parameter gatt_if : application if +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** num_handles : number of handles needed by the service. +** is_pri : is a primary service or not. +** +** Returns service handle if sucessful, otherwise 0. +** +*******************************************************************************/ +UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst, UINT16 num_handles, BOOLEAN is_pri) +{ + + tGATT_HDL_LIST_INFO *p_list_info= &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list=NULL; + UINT16 s_hdl=0; + BOOLEAN save_hdl=FALSE; + tGATTS_PENDING_NEW_SRV_START *p_buf=NULL; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + + GATT_TRACE_API0 ("GATTS_CreateService" ); + + if (p_reg == NULL) + { + GATT_TRACE_ERROR1 ("Inavlid gatt_if=%d", gatt_if); + return(0); + } + + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) + { + s_hdl = p_list->asgn_range.s_handle; + GATT_TRACE_DEBUG0 ("Service already been created!!"); + } + else + { + if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) + { + s_hdl= gatt_cb.hdl_cfg.gatt_start_hdl; + } + else if ((p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GAP_SERVER)) + { + s_hdl= gatt_cb.hdl_cfg.gap_start_hdl; + } + else + { + p_list = p_list_info->p_first; + + if (p_list) + { + s_hdl = p_list->asgn_range.e_handle + 1; + } + + if (s_hdl < gatt_cb.hdl_cfg.app_start_hdl) + { + + s_hdl= gatt_cb.hdl_cfg.app_start_hdl; + } + save_hdl = TRUE; + } + + /* check for space */ + if (num_handles > (0xFFFF - s_hdl + 1)) + { + GATT_TRACE_ERROR2 ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u", s_hdl, num_handles); + return(0); + } + + if ( (p_list = gatt_alloc_hdl_buffer()) == NULL) + { + /* No free entry */ + GATT_TRACE_ERROR0 ("GATTS_ReserveHandles: no free handle blocks"); + return(0); + } + + p_list->asgn_range.app_uuid128 = *p_app_uuid128; + p_list->asgn_range.svc_uuid = *p_svc_uuid; + p_list->asgn_range.svc_inst = svc_inst; + p_list->asgn_range.s_handle = s_hdl; + p_list->asgn_range.e_handle = s_hdl+num_handles-1; + p_list->asgn_range.is_primary = is_pri; + + gatt_add_an_item_to_list(p_list_info, p_list); + + if (save_hdl) + { + if (gatt_cb.cb_info.p_nv_save_callback) + (*gatt_cb.cb_info.p_nv_save_callback)(TRUE, &p_list->asgn_range); + /* add a pending new service change item to the list */ + if ( (p_buf = gatt_add_pending_new_srv_start(&p_list->asgn_range)) == NULL) + { + /* No free entry */ + GATT_TRACE_ERROR0 ("gatt_add_pending_new_srv_start: no free blocks"); + + if (p_list) + { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_hdl_buffer(p_list); + } + return(0); + } + + GATT_TRACE_DEBUG0 ("Add a new srv chg item"); + } + } + + if (!gatts_init_service_db(&p_list->svc_db, *p_svc_uuid, is_pri, s_hdl , num_handles)) + { + GATT_TRACE_ERROR0 ("GATTS_ReserveHandles: service DB initialization failed"); + if (p_list) + { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_hdl_buffer(p_list); + } + + if (p_buf) + GKI_freebuf (GKI_remove_from_queue (&gatt_cb.pending_new_srv_start_q, p_buf)); + return(0); + } + + GATT_TRACE_DEBUG6 ("GATTS_CreateService(success): handles needed:%u s_hdl=%u e_hdl=%u %s[%x] is_primary=%d", + num_handles, p_list->asgn_range.s_handle , p_list->asgn_range.e_handle, + ((p_list->asgn_range.svc_uuid.len == 2) ? "uuid16": "uuid128" ), + p_list->asgn_range.svc_uuid.uu.uuid16, + p_list->asgn_range.is_primary); + + return(s_hdl); +} + +/******************************************************************************* +** +** Function GATTS_AddIncludeService +** +** Description This function is called to add an included service. +** +** Parameter service_handle : To which service this included service is added to. +** include_svc_handle : included service handle. +** +** Returns included service attribute handle. If 0, add included service +** fail. +** +*******************************************************************************/ +UINT16 GATTS_AddIncludeService (UINT16 service_handle, UINT16 include_svc_handle) + +{ + tGATT_HDL_LIST_ELEM *p_decl, *p_incl_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) + { + GATT_TRACE_DEBUG0("Service not created"); + return 0; + } + if ((p_incl_decl = gatt_find_hdl_buffer_by_handle(include_svc_handle)) == NULL) + { + GATT_TRACE_DEBUG0("Included Service not created"); + return 0; + } + + return gatts_add_included_service(&p_decl->svc_db, + p_incl_decl->asgn_range.s_handle, + p_incl_decl->asgn_range.e_handle, + p_incl_decl->asgn_range.svc_uuid); +} +/******************************************************************************* +** +** Function GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** It will add a characteristic declaration and characteristic +** value declaration into the service database identified by the +** service handle. +** +** Parameter service_handle : To which service this included service is added to. +** char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns Characteristic value declaration attribute handle. 0 if failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm,tGATT_CHAR_PROP property) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) + { + GATT_TRACE_DEBUG0("Service not created"); + return 0; + } + /* data validity checking */ + if ( ((property & GATT_CHAR_PROP_BIT_AUTH) && !(perm & GATT_WRITE_SIGNED_PERM)) || + ((perm & GATT_WRITE_SIGNED_PERM) && !(property & GATT_CHAR_PROP_BIT_AUTH)) ) + { + GATT_TRACE_DEBUG2("Invalid configuration property=0x%x perm=0x%x ", property, perm); + return 0; + } + + return gatts_add_characteristic(&p_decl->svc_db, + perm, + property, + p_char_uuid); +} +/******************************************************************************* +** +** Function GATTS_AddCharDescriptor +** +** Description This function is called to add a characteristic descriptor +** into a service database. Add descriptor should follow add char +** to which it belongs, and next add char should be done only +** after all add descriptors for the previous char. +** +** Parameter service_handle : To which service this characteristic descriptor +** is added to. +** perm : Characteristic value declaration attribute +** permission. +** p_descr_uuid : Characteristic descriptor UUID +** +** Returns Characteristic descriptor attribute handle. 0 if add +** characteristic descriptor failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, + tGATT_PERM perm, + tBT_UUID * p_descr_uuid) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) + { + GATT_TRACE_DEBUG0("Service not created"); + return 0; + } + if (p_descr_uuid == NULL || + (p_descr_uuid->len != LEN_UUID_128 && p_descr_uuid->len != LEN_UUID_16)) + { + GATT_TRACE_DEBUG0("Illegal parameter"); + return 0; + } + + return gatts_add_char_descr(&p_decl->svc_db, + perm, + p_descr_uuid); + +} +/******************************************************************************* +** +** Function GATTS_DeleteService +** +** Description This function is called to delete a service. +** +** Parameter gatt_if : application interface +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** +** Returns TRUE if operation succeed, FALSE if handle block was not found. +** +*******************************************************************************/ +BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + + tGATT_HDL_LIST_INFO *p_list_info= &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list=NULL; + UINT8 i_sreg; + tGATTS_PENDING_NEW_SRV_START *p_buf; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + GATT_TRACE_DEBUG0 ("GATTS_DeleteService"); + + if (p_reg == NULL) + { + GATT_TRACE_ERROR0 ("Applicaiton not foud"); + return(FALSE); + } + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) == NULL) + { + GATT_TRACE_ERROR0 ("No Service found"); + return(FALSE); + } + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) + { + GATT_TRACE_DEBUG0 ("Delete a new service changed item - the service has not yet started"); + GKI_freebuf (GKI_remove_from_queue (&gatt_cb.pending_new_srv_start_q, p_buf)); + } + else + { + gatt_proc_srv_chg(); + } + + if ((i_sreg = gatt_sr_find_i_rcb_by_app_id (p_app_uuid128, + p_svc_uuid, + svc_inst)) != GATT_MAX_SR_PROFILES) + { + GATTS_StopService(gatt_cb.sr_reg[i_sreg].s_hdl); + } + + GATT_TRACE_DEBUG2 ("released handles s_hdl=%u e_hdl=%u", + p_list->asgn_range.s_handle , p_list->asgn_range.e_handle ); + + if ( (p_list->asgn_range.s_handle >= gatt_cb.hdl_cfg.app_start_hdl) + && gatt_cb.cb_info.p_nv_save_callback) + (*gatt_cb.cb_info.p_nv_save_callback)(FALSE, &p_list->asgn_range); + + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_hdl_buffer(p_list); + + return(TRUE); +} + +/******************************************************************************* +** +** Function GATTS_StartService +** +** Description This function is called to start a service with GATT +** +** Parameter gatt_if : service handle. +** p_cback : application service callback functions. +** sup_transport : supported transport(s) for this primary service +** +** return GATT_SUCCESS if sucessfully started; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, + tGATT_TRANSPORT sup_transport) +{ + tGATT_SR_REG *p_sreg; + tGATT_HDL_LIST_ELEM *p_list=NULL; + UINT8 i_sreg; + tBT_UUID *p_uuid; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_API0 ("GATTS_StartService"); + + if (p_reg == NULL) + { + /* Not found */ + GATT_TRACE_ERROR0 ("Applicaiton not found "); + return GATT_NOT_FOUND; + } + + if ((p_list = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) + { + /* Not found */ + GATT_TRACE_ERROR0 ("no service found"); + return GATT_NOT_FOUND; + } + + if (gatt_sr_find_i_rcb_by_app_id (&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst) != GATT_MAX_SR_PROFILES) + { + GATT_TRACE_ERROR0 ("Duplicate Service start - Service already started"); + return GATT_SERVICE_STARTED; + } + + /*this is a new application servoce start */ + if ((i_sreg = gatt_sr_alloc_rcb(p_list)) == GATT_MAX_SR_PROFILES) + { + GATT_TRACE_ERROR0 ("GATTS_StartService: no free server registration block"); + return GATT_NO_RESOURCES; + } + + p_sreg = &gatt_cb.sr_reg[i_sreg]; + p_sreg->gatt_if = gatt_if; + + switch (sup_transport) + { + case GATT_TRANSPORT_BR_EDR: + case GATT_TRANSPORT_LE_BR_EDR: + if (p_sreg->type == GATT_UUID_PRI_SERVICE) + { + p_uuid = gatts_get_service_uuid (p_sreg->p_db); + + p_sreg->sdp_handle = gatt_add_sdp_record(p_uuid, p_sreg->s_hdl, p_sreg->e_hdl); + } + break; + default: + break; + } + + gatts_update_srv_list_elem(i_sreg, p_sreg->s_hdl, + p_list->asgn_range.is_primary); + + gatt_add_a_srv_to_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[i_sreg]); + + GATT_TRACE_DEBUG1 ("allocated i_sreg=%d ",i_sreg); + + GATT_TRACE_DEBUG5 ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x", + p_sreg->s_hdl,p_sreg->e_hdl, + p_sreg->type, p_sreg->service_instance, + p_sreg->sdp_handle); + + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) + { + gatt_proc_srv_chg(); + /* remove the new service element after the srv changed processing is completed*/ + + GKI_freebuf (GKI_remove_from_queue (&gatt_cb.pending_new_srv_start_q, p_buf)); + } + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function GATTS_StopService +** +** Description This function is called to stop a service +** +** Parameter service_handle : this is the start handle of a service +** +** Returns None. +** +*******************************************************************************/ +void GATTS_StopService (UINT16 service_handle) +{ + UINT8 ii = gatt_sr_find_i_rcb_by_handle(service_handle); + + GATT_TRACE_API1("GATTS_StopService %u", service_handle); + + /* Index 0 is reserved for GATT, and is never stopped */ + if ( (ii > 0) && (ii < GATT_MAX_SR_PROFILES) && (gatt_cb.sr_reg[ii].in_use) ) + { + if (gatt_cb.sr_reg[ii].sdp_handle) + { + SDP_DeleteRecord(gatt_cb.sr_reg[ii].sdp_handle); + } + gatt_remove_a_srv_from_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[ii]); + gatt_cb.srv_list[ii].in_use = FALSE; + memset (&gatt_cb.sr_reg[ii], 0, sizeof(tGATT_SR_REG)); + } + else + { + GATT_TRACE_ERROR1("GATTS_StopService service_handle: %u is not in use", service_handle); + } +} +/******************************************************************************* +** +** Function GATTs_HandleValueIndication +** +** Description This function sends a handle value indication to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if sucessfully sent or queued; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_status = GATT_ILLEGAL_PARAMETER; + + tGATT_VALUE indication; + BT_HDR *p_msg; + tGATT_VALUE *p_buf; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + + GATT_TRACE_API0 ("GATTS_HandleValueIndication"); + if ( (p_reg == NULL) || (p_tcb == NULL)) + { + GATT_TRACE_ERROR1 ("GATTS_HandleValueIndication Unknown conn_id: %u ", conn_id); + return(tGATT_STATUS) GATT_INVALID_CONN_ID; + } + indication.conn_id = conn_id; + indication.handle = attr_handle; + indication.len = val_len; + memcpy (indication.value, p_val, val_len); + indication.auth_req = GATT_AUTH_REQ_NONE; + + if (GATT_HANDLE_IS_VALID (attr_handle) ) + { + if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) + { + GATT_TRACE_DEBUG0 ("Add a pending indication"); + if ((p_buf = gatt_add_pending_ind(p_tcb, &indication)) !=NULL) + { + cmd_status = GATT_SUCCESS; + } + else + { + cmd_status = GATT_NO_RESOURCES; + } + } + else + { + + if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) + { + cmd_status = attp_send_sr_msg (p_tcb, p_msg); + + if (cmd_status == GATT_SUCCESS) + { + p_tcb->indicate_handle = indication.handle; + gatt_start_conf_timer(p_tcb); + } + } + } + } + return cmd_status; +} + +/******************************************************************************* +** +** Function GATTS_HandleValueNotification +** +** Description This function sends a handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + BT_HDR *p_buf; + tGATT_VALUE notif; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API0 ("GATTS_HandleValueNotification"); + + if ( (p_reg == NULL) || (p_tcb == NULL)) + { + GATT_TRACE_ERROR1 ("GATTS_HandleValueNotification Unknown conn_id: %u ", conn_id); + return(tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (GATT_HANDLE_IS_VALID (attr_handle)) + { + notif.handle = attr_handle; + notif.len = val_len; + memcpy (notif.value, p_val, val_len); + notif.auth_req = GATT_AUTH_REQ_NONE;; + + p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_NOTIF, (tGATT_SR_MSG *)¬if); + cmd_sent = attp_send_sr_msg (p_tcb, p_buf); + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function GATTS_SendRsp +** +** Description This function sends the server response to client. +** +** Parameter conn_id: connection identifier. +** trans_id: transaction id +** status: response status +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API3 ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x", + conn_id, trans_id, status); + + if ( (p_reg == NULL) || (p_tcb == NULL)) + { + GATT_TRACE_ERROR1 ("GATTS_SendRsp Unknown conn_id: %u ", conn_id); + return(tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (p_tcb->sr_cmd.trans_id != trans_id) + { + GATT_TRACE_ERROR2 ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x", + conn_id, p_tcb->sr_cmd.op_code); + + return(GATT_WRONG_STATE); + } + /* Process App response */ + cmd_sent = gatt_sr_process_app_rsp (p_tcb, gatt_if, trans_id, p_tcb->sr_cmd.op_code, status, p_msg); + + return cmd_sent; +} + +/*******************************************************************************/ +/* GATT Profile Srvr Functions */ +/*******************************************************************************/ + +/*******************************************************************************/ +/* */ +/* GATT CLIENT APIs */ +/* */ +/*******************************************************************************/ + + +/******************************************************************************* +** +** Function GATTC_ConfigureMTU +** +** Description This function is called to configure the ATT MTU size. +** +** Parameters conn_id: connection identifier. +** mtu - attribute MTU size.. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ConfigureMTU (UINT16 conn_id, UINT16 mtu) +{ + UINT8 ret = GATT_NO_RESOURCES; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATT_CLCB *p_clcb; + + GATT_TRACE_API2 ("GATTC_ConfigureMTU conn_id=%d mtu=%d", conn_id, mtu ); + + // Validate that the link is BLE, not BR/EDR + // ???? + + if ( (p_tcb == NULL) || (p_reg==NULL) || (mtu < GATT_DEF_BLE_MTU_SIZE) || (mtu > GATT_MAX_MTU_SIZE)) + { + return GATT_ILLEGAL_PARAMETER; + } + + if (gatt_is_clcb_allocated(conn_id)) + { + GATT_TRACE_ERROR1("GATTC_ConfigureMTU GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) + { + p_clcb->p_tcb->payload_size = mtu; + p_clcb->operation = GATTC_OPTYPE_CONFIG; + + ret = attp_send_cl_msg (p_clcb->p_tcb, p_clcb->clcb_idx, GATT_REQ_MTU, (tGATT_CL_MSG *)&mtu); + } + + return ret; +} + +/******************************************************************************* +** +** Function GATTC_Discover +** +** Description This function is called to do a discovery procedure on ATT server. +** +** Parameters conn_id: connection identifier. +** disc_type:discovery type. +** p_param: parameters of discovery requirement. +** +** Returns GATT_SUCCESS if command received/sent successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Discover (UINT16 conn_id, tGATT_DISC_TYPE disc_type, + tGATT_DISC_PARAM *p_param) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API2 ("GATTC_Discover conn_id=%d disc_type=%d",conn_id, disc_type); + + if ( (p_tcb == NULL) || (p_reg==NULL) ||(p_param == NULL) || + (disc_type >= GATT_DISC_MAX)) + { + GATT_TRACE_ERROR2("GATTC_Discover Illegal param: disc_type %d conn_id = %d", disc_type, conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + + if (gatt_is_clcb_allocated(conn_id)) + { + GATT_TRACE_ERROR1("GATTC_Discover GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) + { + if (!GATT_HANDLE_IS_VALID(p_param->s_handle) || + !GATT_HANDLE_IS_VALID(p_param->e_handle) || + /* search by type does not have a valid UUID param */ + (disc_type == GATT_DISC_SRVC_BY_UUID && + p_param->service.len == 0)) + { + return GATT_ILLEGAL_PARAMETER; + } + + p_clcb->operation = GATTC_OPTYPE_DISCOVERY; + p_clcb->op_subtype = disc_type; + p_clcb->s_handle = p_param->s_handle; + p_clcb->e_handle = p_param->e_handle; + p_clcb->uuid = p_param->service; + + gatt_act_discovery(p_clcb); + } + else + { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Read +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute read type. +** p_read - read operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, tGATT_READ_PARAM *p_read) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_READ_MULTI *p_read_multi; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API2 ("GATTC_Read conn_id=%d type=%d", conn_id, type); + + if ( (p_tcb == NULL) || (p_reg==NULL) || (p_read == NULL) || ((type >= GATT_READ_MAX) || (type == 0))) + { + GATT_TRACE_ERROR2("GATT_Read Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (gatt_is_clcb_allocated(conn_id)) + { + GATT_TRACE_ERROR1("GATTC_Read GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ( (p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) + { + p_clcb->operation = GATTC_OPTYPE_READ; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_read->by_handle.auth_req; + p_clcb->counter = 0; + + switch (type) + { + case GATT_READ_BY_TYPE: + case GATT_READ_CHAR_VALUE: + p_clcb->s_handle = p_read->service.s_handle; + p_clcb->e_handle = p_read->service.e_handle; + memcpy(&p_clcb->uuid, &p_read->service.uuid, sizeof(tBT_UUID)); + break; + case GATT_READ_MULTIPLE: + p_clcb->s_handle = 0; + /* copy multiple handles in CB */ + p_read_multi = (tGATT_READ_MULTI *)GKI_getbuf(sizeof(tGATT_READ_MULTI)); + p_clcb->p_attr_buf = (UINT8*)p_read_multi; + memcpy (p_read_multi, &p_read->read_multiple, sizeof(tGATT_READ_MULTI)); + case GATT_READ_BY_HANDLE: + case GATT_READ_PARTIAL: + memset(&p_clcb->uuid, 0, sizeof(tBT_UUID)); + p_clcb->s_handle = p_read->by_handle.handle; + + if (type == GATT_READ_PARTIAL) + { + p_clcb->counter = p_read->partial.offset; + } + + break; + default: + break; + } + /* start security check */ + if (gatt_security_check_start(p_clcb) == FALSE) + { + status = GATT_NO_RESOURCES; + gatt_clcb_dealloc(p_clcb); + } + } + else + { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Write +** +** Description This function is called to write the value of an attribute to +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute write type. +** p_write - write operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_VALUE *p; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if ( (p_tcb == NULL) || (p_reg==NULL) || (p_write == NULL) || + ((type != GATT_WRITE) && (type != GATT_WRITE_PREPARE) && (type != GATT_WRITE_NO_RSP)) ) + { + GATT_TRACE_ERROR2("GATT_Write Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (gatt_is_clcb_allocated(conn_id)) + { + GATT_TRACE_ERROR1("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) + { + p_clcb->operation = GATTC_OPTYPE_WRITE; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_write->auth_req; + + if (( p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL) + { + memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE)); + + p = (tGATT_VALUE *)p_clcb->p_attr_buf; + if (type == GATT_WRITE_PREPARE) + { + p_clcb->start_offset = p_write->offset; + p->offset = 0; + } + + if (gatt_security_check_start(p_clcb) == FALSE) + { + status = GATT_NO_RESOURCES; + } + } + else + { + status = GATT_NO_RESOURCES; + } + + if (status == GATT_NO_RESOURCES) + gatt_clcb_dealloc(p_clcb); + } + else + { + status = GATT_NO_RESOURCES; + } + return status; +} + + +/******************************************************************************* +** +** Function GATTC_ExecuteWrite +** +** Description This function is called to send an Execute write request to +** the server. +** +** Parameters conn_id: connection identifier. +** is_execute - to execute or cancel the prepare write requet(s) +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_EXEC_FLAG flag; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + GATT_TRACE_API2 ("GATTC_ExecuteWrite conn_id=%d is_execute=%d", conn_id, is_execute); + + if ( (p_tcb == NULL) || (p_reg==NULL) ) + { + GATT_TRACE_ERROR1("GATTC_ExecuteWrite Illegal param: conn_id %d", conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + if (gatt_is_clcb_allocated(conn_id)) + { + GATT_TRACE_ERROR1("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) + { + p_clcb->operation = GATTC_OPTYPE_EXE_WRITE; + flag = is_execute ? GATT_PREP_WRITE_EXEC : GATT_PREP_WRITE_CANCEL; + gatt_send_queue_write_cancel (p_clcb->p_tcb, p_clcb, flag); + } + else + { + GATT_TRACE_ERROR1("Unable to allocate client CB for conn_id %d ", conn_id); + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_SendHandleValueConfirm +** +** Description This function is called to send a handle value confirmation +** as response to a handle value notification from server. +** +** Parameters conn_id: connection identifier. +** handle: the handle of the attribute confirmation. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb=gatt_get_tcb_by_idx(GATT_GET_TCB_IDX(conn_id)); + + GATT_TRACE_API2 ("GATTC_SendHandleValueConfirm conn_id=%d handle=0x%x", conn_id, handle); + + if (p_tcb) + { + if (p_tcb->ind_count > 0 ) + { + btu_stop_timer (&p_tcb->ind_ack_timer_ent); + + GATT_TRACE_DEBUG1 ("notif_count=%d ", p_tcb->ind_count); + /* send confirmation now */ + ret = attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, (tGATT_CL_MSG *)&handle); + + p_tcb->ind_count = 0; + + } + else + { + GATT_TRACE_DEBUG1 ("GATTC_SendHandleValueConfirm - conn_id: %u - ignored not waiting for indicaiton ack", conn_id); + ret = GATT_SUCCESS; + } + } + else + { + GATT_TRACE_ERROR1 ("GATTC_SendHandleValueConfirm - Unknown conn_id: %u", conn_id); + } + return ret; +} + + +/*******************************************************************************/ +/* */ +/* GATT APIs */ +/* */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function GATT_SetIdleTimeout +** +** Description This function (common to both client and server) sets the idle +** timeout for a tansport connection +** +** Parameter bd_addr: target device bd address. +** idle_tout: timeout value in seconds. +** +** Returns void +** +*******************************************************************************/ +void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout) +{ + tGATT_TCB *p_tcb; + BOOLEAN status = FALSE; + + if ((p_tcb = gatt_find_tcb_by_addr (bd_addr)) != NULL) + { + if (p_tcb->att_lcid == L2CAP_ATT_CID) + { + status = L2CA_SetFixedChannelTout (bd_addr, L2CAP_ATT_CID, idle_tout); + } + else + { + status = L2CA_SetIdleTimeout (p_tcb->att_lcid, idle_tout, FALSE); + } + } + + GATT_TRACE_API2 ("GATT_SetIdleTimeout idle_tout=%d status=%d(1-OK 0-not performed)", + idle_tout, status); +} + + +/******************************************************************************* +** +** Function GATT_Register +** +** Description This function is called to register an application +** with GATT +** +** Parameter p_app_uuid128: Application UUID +** p_cb_info: callback functions. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ +tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info) +{ + tGATT_REG *p_reg; + UINT8 i_gatt_if=0; + tGATT_IF gatt_if=0; + + GATT_TRACE_API0 ("GATT_Register"); + gatt_dbg_display_uuid(*p_app_uuid128); + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) + { + if (p_reg->in_use && !memcmp(p_app_uuid128->uu.uuid128, p_reg->app_uuid128.uu.uuid128, LEN_UUID_128)) + { + GATT_TRACE_ERROR0("application already registered."); + return 0; + } + } + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) + { + if (!p_reg->in_use) + { + memset(p_reg, 0 , sizeof(tGATT_REG)); + i_gatt_if++; /* one based number */ + p_reg->app_uuid128 = *p_app_uuid128; + gatt_if = + p_reg->gatt_if = (tGATT_IF)i_gatt_if; + p_reg->app_cb = *p_cb_info; + p_reg->in_use = TRUE; + + break; + } + } + GATT_TRACE_API1 ("allocated gatt_if=%d", gatt_if); + return gatt_if; +} + + +/******************************************************************************* +** +** Function GATT_Deregister +** +** Description This function deregistered the application from GATT. +** +** Parameters gatt_if: applicaiton interface. +** +** Returns None. +** +*******************************************************************************/ +void GATT_Deregister (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb; + tGATT_CLCB *p_clcb; + UINT8 i, ii, j; + tGATT_SR_REG *p_sreg; + + GATT_TRACE_API1 ("GATT_Deregister gatt_if=%d", gatt_if); + /* Index 0 is GAP and is never deregistered */ + if ( (gatt_if == 0) || (p_reg == NULL) ) + { + GATT_TRACE_ERROR1 ("GATT_Deregister with invalid gatt_if: %u", gatt_if); + return; + } + + /* stop all services */ + /* todo an applcaiton can not be deregistered if its services is also used by other application + deregisteration need to bed performed in an orderly fashion + no check for now */ + + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) + { + if (p_sreg->in_use && (p_sreg->gatt_if == gatt_if)) + { + GATTS_StopService(p_sreg->s_hdl); + } + } + + /* free all services db buffers if owned by this application */ + gatt_free_srvc_db_buffer_app_id(&p_reg->app_uuid128); + + /* When an application deregisters, check remove the link associated with the app */ + + for (i=0, p_tcb = gatt_cb.tcb; i < GATT_MAX_PHY_CHANNEL; i++, p_tcb++) + { + if (p_tcb->in_use) + { + if (gatt_get_ch_state(p_tcb) != GATT_CH_CLOSE) + { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) + { + /* this will disconnect the link or cancel the pending connect request at lower layer*/ + gatt_disconnect(p_tcb->peer_bda); + } + } + + for (j = 0, p_clcb= &gatt_cb.clcb[j]; j < GATT_CL_MAX_LCB; j++, p_clcb++) + { + if (p_clcb->in_use && + (p_clcb->p_reg->gatt_if == gatt_if) && + (p_clcb->p_tcb->tcb_idx == p_tcb->tcb_idx)) + { + gatt_clcb_dealloc (p_clcb); + break; + } + } + } + } + + gatt_deregister_bgdev_list(gatt_if); + memset (p_reg, 0, sizeof(tGATT_REG)); +} + + +/******************************************************************************* +** +** Function GATT_StartIf +** +** Description This function is called after registration to start receiving +** callbacks for registered interface. Function may call back +** with connection status and queued notifications +** +** Parameter gatt_if: applicaiton interface. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ +void GATT_StartIf (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + //tGATT_CLCB *p_clcb; + BD_ADDR bda; + UINT8 start_idx, found_idx; + UINT16 conn_id; + + GATT_TRACE_API1 ("GATT_StartIf gatt_if=%d", gatt_if); + if ((p_reg = gatt_get_regcb(gatt_if)) != NULL) + { + p_reg = &gatt_cb.cl_rcb[gatt_if - 1]; + start_idx = 0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx)) + { + p_tcb = gatt_find_tcb_by_addr(bda); + if (p_reg->app_cb.p_conn_cb) + { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + (*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, TRUE, 0); + } + start_idx = ++found_idx; + } + } +} + + +/******************************************************************************* +** +** Function GATT_Connect +** +** Description This function initiate a connecttion to a ATT server. +** +** Parameters gatt_if: applicaiton interface +** bd_addr: peer device address. +** is_direct: is a direct conenection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct){ + tGATT_REG *p_reg; + BOOLEAN status; + + GATT_TRACE_API1 ("GATT_Connect gatt_if=%d", gatt_if); + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) + { + GATT_TRACE_ERROR1("GATT_Connect - gatt_if =%d is not registered", gatt_if); + return(FALSE); + } + + if (is_direct) + status = gatt_act_connect (p_reg, bd_addr); + else + status = gatt_update_auto_connect_dev(gatt_if,TRUE, bd_addr); + + return status; + +} + +/******************************************************************************* +** +** Function GATT_CancelConnect +** +** Description This function initiate a connecttion to a ATT server. +** +** Parameters gatt_if: client interface. If 0 used as unconditionally disconnect, +** typically used for direct connection cancellation. +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_CancelConnect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct){ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + BOOLEAN status = TRUE; + tGATT_IF temp_gatt_if; + UINT8 start_idx, found_idx; + + GATT_TRACE_API1 ("GATT_CancelConnect gatt_if=%d", gatt_if); + + if ((gatt_if != 0) && ((p_reg = gatt_get_regcb(gatt_if)) == NULL)) + { + GATT_TRACE_ERROR1("GATT_CancelConnect - gatt_if =%d is not registered", gatt_if); + return(FALSE); + } + + if (is_direct) + { + if (!gatt_if) + { + GATT_TRACE_DEBUG0("GATT_CancelConnect - unconditional"); + start_idx = 0; + p_tcb = gatt_find_tcb_by_addr(bd_addr); + if (p_tcb && gatt_num_apps_hold_link(p_tcb)) + { + while (status && gatt_find_app_hold_link(p_tcb, start_idx, &found_idx, &temp_gatt_if)) + { + status = gatt_cancel_open(temp_gatt_if, bd_addr); + start_idx = ++found_idx; + } + } + else + { + GATT_TRACE_ERROR0("GATT_CancelConnect - no app found"); + status = FALSE; + } + } + else + { + status = gatt_cancel_open(gatt_if, bd_addr); + } + } + else + { + if (!gatt_if) + { + if (gatt_get_num_apps_for_bg_dev(bd_addr)) + { + while (gatt_find_app_for_bg_dev(bd_addr, &temp_gatt_if)) + gatt_remove_bg_dev_for_app(temp_gatt_if, bd_addr); + } + else + { + GATT_TRACE_ERROR0("GATT_CancelConnect -no app associated with the bg device for unconditional removal"); + status = FALSE; + } + } + else + { + status = gatt_remove_bg_dev_for_app(gatt_if, bd_addr); + } + } + + return status; +} + +/******************************************************************************* +** +** Function GATT_Disconnect +** +** Description This function disconnect a logic channel. +** +** Parameters conn_id: connection identifier. +** +** Returns GATT_SUCCESS if disconnected. +** +*******************************************************************************/ +tGATT_STATUS GATT_Disconnect (UINT16 conn_id) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb=NULL; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + + GATT_TRACE_API1 ("GATT_Disconnect conn_id=%d ", conn_id); + + p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + if (p_tcb) + { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) + { + gatt_disconnect(p_tcb->peer_bda); + } + ret = GATT_SUCCESS; + } + return ret; +} + + +/******************************************************************************* +** +** Function GATT_GetConnectionInfor +** +** Description This function use conn_id to find its associated BD address and applciation +** interface +** +** Parameters conn_id: connection id (input) +** p_gatt_if: applicaiton interface (output) +** bd_addr: peer device address. (output) +** +** Returns TRUE the ligical link information is found for conn_id +** +*******************************************************************************/ +BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, BD_ADDR bd_addr) +{ + + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb= gatt_get_tcb_by_idx(tcb_idx); + BOOLEAN status=FALSE; + + GATT_TRACE_API1 ("GATT_GetConnectionInfor conn_id=%d", conn_id); + + if (p_tcb && p_reg ) + { + memcpy(bd_addr, p_tcb->peer_bda, BD_ADDR_LEN); + *p_gatt_if = gatt_if; + status = TRUE; + } + return status; +} + + +/******************************************************************************* +** +** Function GATT_GetConnIdIfConnected +** +** Description This function find the conn_id if the logical link for BD address +** and applciation interface is connected +** +** Parameters gatt_if: applicaiton interface (input) +** bd_addr: peer device address. (input) +** p_conn_id: connection id (output) +** +** Returns TRUE the ligical link is connected +** +*******************************************************************************/ +BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, UINT16 *p_conn_id) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb= gatt_find_tcb_by_addr(bd_addr); + BOOLEAN status=FALSE; + + if (p_reg && p_tcb && (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) ) + { + *p_conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + status = TRUE; + } + + GATT_TRACE_API1 ("GATT_GetConnIdIfConnected status=%d", status); + return status; +} + +#endif + + diff --git a/stack/gatt/gatt_attr.c b/stack/gatt/gatt_attr.c new file mode 100644 index 0000000..b7ec2fc --- /dev/null +++ b/stack/gatt/gatt_attr.c @@ -0,0 +1,278 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT server attributes access request + * handling functions. + * + ******************************************************************************/ + +#include "bt_target.h" + +#include "gatt_api.h" +#include "gatt_int.h" + +#if BLE_INCLUDED == TRUE + +#define GATTP_MAX_NUM_INC_SVR 0 +#define GATTP_MAX_CHAR_NUM 2 +#define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1) +#define GATTP_MAX_CHAR_VALUE_SIZE 50 + +#ifndef GATTP_ATTR_DB_SIZE +#define GATTP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, GATTP_MAX_CHAR_VALUE_SIZE) +#endif + +static void gatt_profile_request_cback (UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); +static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, tGATT_DISCONN_REASON reason); + +static tGATT_CBACK gatt_profile_cback = +{ + gatt_profile_connect_cback, + NULL, + NULL, + NULL, + gatt_profile_request_cback +} ; + +/******************************************************************************* +** +** Function gatt_profile_find_conn_id_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) + { + return p_clcb->conn_id; + } + } + + return GATT_INVALID_CONN_ID; +} + +/******************************************************************************* +** +** Function gatt_profile_find_clcb_by_bd_addr +** +** Description The function searches all LCBs with macthing bd address. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) + { + return p_clcb; + } + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_profile_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda) +{ + UINT8 i_clcb = 0; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) + { + if (!p_clcb->in_use) + { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + return p_clcb; +} +/******************************************************************************* +** +** Function gatt_profile_clcb_dealloc +** +** Description The function deallocates a GATT profile connection link control block +** +** Returns NTrue the deallocation is successful +** +*******************************************************************************/ +BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id) +{ + UINT8 i_clcb = 0; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) + { + memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB)); + return TRUE; + } + } + return FALSE; +} + + +/******************************************************************************* +** +** Function gatt_profile_request_cback +** +** Description GATT profile attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +static void gatt_profile_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; + + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + switch (type) + { + case GATTS_REQ_TYPE_READ: + status = GATT_READ_NOT_PERMIT; + break; + + case GATTS_REQ_TYPE_WRITE: + status = GATT_WRITE_NOT_PERMIT; + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + //case GATT_CMD_WRITE: + ignore = TRUE; + GATT_TRACE_EVENT0("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); + break; + + case GATTS_REQ_TYPE_MTU: + GATT_TRACE_EVENT1("Get MTU exchange new mtu size: %d", p_data->mtu); + ignore = TRUE; + break; + + default: + GATT_TRACE_EVENT1("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + if (!ignore) + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + +} + +/******************************************************************************* +** +** Function gatt_profile_connect_cback +** +** Description Gatt profile connection callback. +** +** Returns void +** +*******************************************************************************/ +static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason) +{ + GATT_TRACE_EVENT5 ("gatt_profile_connect_cback: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", + (bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3], + (bda[4]<<8)+bda[5], connected, conn_id, reason); + + if (connected) + { + if (gatt_profile_clcb_alloc(conn_id, bda) == NULL) + { + GATT_TRACE_ERROR0 ("gatt_profile_connect_cback: no_resource"); + return; + } + } + else + { + gatt_profile_clcb_dealloc(conn_id); + } + +} + +/******************************************************************************* +** +** Function gatt_profile_db_init +** +** Description Initializa the GATT profile attribute database. +** +*******************************************************************************/ +void gatt_profile_db_init (void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; + UINT16 service_handle = 0; + tGATT_STATUS status; + + /* Fill our internal UUID with a fixed pattern 0x81 */ + memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); + + + /* Create a GATT profile service */ + gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback); + GATT_StartIf(gatt_cb.gatt_if); + + service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); + /* add Service Changed characteristic + */ + uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; + gatt_cb.gattp_attr.service_change = 0; + gatt_cb.gattp_attr.handle = + gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE); + + GATT_TRACE_DEBUG1 ("gatt_profile_db_init: handle of service changed%d", + gatt_cb.handle_of_h_r ); + + /* start service + */ + status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATT_TRANSPORT_LE_BR_EDR); + + GATT_TRACE_DEBUG2 ("gatt_profile_db_init: gatt_if=%d start status%d", + gatt_cb.gatt_if, status); +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_auth.c b/stack/gatt/gatt_auth.c new file mode 100644 index 0000000..de0c14f --- /dev/null +++ b/stack/gatt/gatt_auth.c @@ -0,0 +1,448 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT authentication handling functions + * + ******************************************************************************/ +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE +#include +#include "gki.h" + +#include "gatt_int.h" +#include "gatt_api.h" +#include "btm_int.h" + +/******************************************************************************* +** +** Function gatt_sign_data +** +** Description This function sign the data for write command. +** +** Returns TRUE if encrypted, otherwise FALSE. +** +*******************************************************************************/ +static BOOLEAN gatt_sign_data (tGATT_CLCB *p_clcb) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + UINT8 *p_data = NULL, *p; + UINT16 payload_size = p_clcb->p_tcb->payload_size; + BOOLEAN status = FALSE; + UINT8 *p_signature; + + p_data = (UINT8 *)GKI_getbuf((UINT16)(p_attr->len + 3)); /* 3 = 2 byte handle + opcode */ + + if (p_data != NULL) + { + p = p_data; + UINT8_TO_STREAM(p, GATT_SIGN_CMD_WRITE); + UINT16_TO_STREAM(p, p_attr->handle); + ARRAY_TO_STREAM(p, p_attr->value, p_attr->len); + + /* sign data length should be attribulte value length plus 2B handle + 1B op code */ + if ((payload_size - GATT_AUTH_SIGN_LEN - 3) < p_attr->len) + p_attr->len = payload_size - GATT_AUTH_SIGN_LEN - 3; + + p_signature = p_attr->value + p_attr->len; + if (BTM_BleDataSignature(p_clcb->p_tcb->peer_bda, + p_data, + (UINT16)(p_attr->len + 3), /* 3 = 2 byte handle + opcode */ + p_signature)) + { + p_attr->len += BTM_BLE_AUTH_SIGN_LEN; + gatt_set_ch_state(p_clcb->p_tcb, GATT_CH_OPEN); + gatt_act_write(p_clcb); + } + else + { + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL); + } + + GKI_freebuf(p_data); + } + + return status; +} + +/******************************************************************************* +** +** Function gatt_verify_signature +** +** Description This function start to verify the sign data when receiving +** the data from peer device. +** +** Returns +** +*******************************************************************************/ +void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf) +{ + UINT16 cmd_len; + UINT8 op_code; + UINT8 *p, *p_orig = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT32 counter; + + cmd_len = p_buf->len - GATT_AUTH_SIGN_LEN + 4; + p = p_orig + cmd_len - 4; + STREAM_TO_UINT32(counter, p); + + if (BTM_BleVerifySignature(p_tcb->peer_bda, p_orig, cmd_len, counter, p)) + { + STREAM_TO_UINT8(op_code, p_orig); + gatt_server_handle_client_req (p_tcb, op_code, (UINT16)(p_buf->len - 1), p_orig); + } + else + { + /* if this is a bad signature, assume from attacker, ignore it */ + GATT_TRACE_ERROR0("Signature Verification Failed"); + gatt_disconnect(p_tcb->peer_bda); + } + + return; +} +/******************************************************************************* +** +** Function gatt_sec_check_complete +** +** Description security check complete and proceed to data sending action. +** +** Returns void. +** +*******************************************************************************/ +void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB *p_clcb) +{ + p_clcb->p_tcb->p_clcb = NULL; + gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE); + + if (!sec_check_ok) + { + gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL); + } + else if (p_clcb->operation == GATTC_OPTYPE_WRITE) + { + gatt_act_write(p_clcb); + } + else if (p_clcb->operation == GATTC_OPTYPE_READ) + { + gatt_act_read(p_clcb, p_clcb->counter); + } +} +/******************************************************************************* +** +** Function gatt_enc_cmpl_cback +** +** Description link encryption complete callback. +** +** Returns +** +*******************************************************************************/ +void gatt_enc_cmpl_cback(BD_ADDR bd_addr, void *p_ref_data, tBTM_STATUS result) +{ + tGATT_TCB *p_tcb; + UINT8 sec_flag; + BOOLEAN status = FALSE; + + GATT_TRACE_DEBUG0("gatt_enc_cmpl_cback"); + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL) + { + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + + if (result == BTM_SUCCESS) + { + if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM ) + { + BTM_GetSecurityFlags(bd_addr, &sec_flag); + if (sec_flag & sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) + { + status = TRUE; + } + } + else + { + status = TRUE; + } + } + gatt_sec_check_complete(status , (tGATT_CLCB *)p_tcb->p_clcb); + } + else + { + GATT_TRACE_ERROR0("enc callback for unknown bd_addr"); + } +} + +/******************************************************************************* +** +** Function gatt_set_sec_act +** +** Description This function set the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act) +{ + if (p_tcb) + { + p_tcb->sec_act = sec_act; + } +} +/******************************************************************************* +** +** Function gatt_get_sec_act +** +** Description This function get the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb) +{ + tGATT_SEC_ACTION sec_act = GATT_SEC_NONE; + if (p_tcb) + { + sec_act = p_tcb->sec_act; + } + return sec_act; +} +/******************************************************************************* +** +** Function gatt_determine_sec_act +** +** Description This routine determine the security action based on auth_request and +** current link status +** +** Returns tGATT_SEC_ACTION security action +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ) +{ + tGATT_SEC_ACTION act = GATT_SEC_OK; + UINT8 sec_flag; + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_AUTH_REQ auth_req = p_clcb->auth_req; + + BOOLEAN is_link_encrypted= FALSE; + BOOLEAN is_le_link=FALSE; + BOOLEAN is_link_key_known=FALSE; + BOOLEAN is_key_mitm=FALSE; + UINT8 key_type; + + if (auth_req == GATT_AUTH_REQ_NONE ) + return act; + + is_le_link = btm_ble_check_link_type(p_tcb->peer_bda); + BTM_GetSecurityFlags(p_tcb->peer_bda, &sec_flag); + + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) + { + is_link_encrypted = TRUE; + } + if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) + { + is_link_key_known = TRUE; + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) + { + is_key_mitm = TRUE; + } + } + + /* first check link key upgrade required or not */ + switch (auth_req) + { + case GATT_AUTH_REQ_MITM: + case GATT_AUTH_REQ_SIGNED_MITM: + if (!is_key_mitm) + act = GATT_SEC_ENCRYPT_MITM; + break; + + case GATT_AUTH_REQ_NO_MITM: + case GATT_AUTH_REQ_SIGNED_NO_MITM: + if (!is_link_key_known) + act = GATT_SEC_ENCRYPT_NO_MITM; + break; + default: + break; + } + + /* now check link needs to be encrypted or not if the link key upgrade is not required */ + if (act == GATT_SEC_OK) + { + if (is_le_link && + (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE_NO_RSP)) + { + /* this is a write command request + check data signing required or not */ + if (!is_link_encrypted) + { + btm_ble_get_enc_key_type(p_tcb->peer_bda, &key_type); + + if ( (key_type & BTM_LE_KEY_LCSRK) && + ((auth_req == GATT_AUTH_REQ_SIGNED_NO_MITM) || + (auth_req == GATT_AUTH_REQ_SIGNED_MITM))) + { + act = GATT_SEC_SIGN_DATA; + } + else + { + act = GATT_SEC_ENCRYPT; + } + } + } + else + { + if (!is_link_encrypted) + { + act = GATT_SEC_ENCRYPT; + } + } + + } + + return act ; + +} + + + +/******************************************************************************* +** +** Function gatt_get_link_encrypt_status +** +** Description This routine get the encryption status of the specified link +** +** +** Returns tGATT_STATUS link encryption status +** +*******************************************************************************/ +tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb) +{ + tGATT_STATUS encrypt_status = GATT_NOT_ENCRYPTED; + UINT8 sec_flag=0; + + BTM_GetSecurityFlags(p_tcb->peer_bda, &sec_flag); + + if ((sec_flag & BTM_SEC_FLAG_ENCRYPTED) && (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) + { + encrypt_status = GATT_ENCRYPED_NO_MITM; + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) + encrypt_status = GATT_ENCRYPED_MITM; + } + + GATT_TRACE_DEBUG1("gatt_get_link_encrypt_status status=0x%x",encrypt_status); + return encrypt_status ; +} + + +/******************************************************************************* +** +** Function gatt_convert_sec_action +** +** Description Convert GATT security action enum into equivalent BTM BLE security action enum +** +** Returns BOOLEAN TRUE - conversation is successful +** +*******************************************************************************/ +static BOOLEAN gatt_convert_sec_action(tGATT_SEC_ACTION gatt_sec_act, tBTM_BLE_SEC_ACT *p_btm_sec_act ) +{ + BOOLEAN status = TRUE; + switch (gatt_sec_act) + { + case GATT_SEC_ENCRYPT: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + case GATT_SEC_ENCRYPT_NO_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + case GATT_SEC_ENCRYPT_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + default: + status = FALSE; + break; + } + + return status; +} +/******************************************************************************* +** +** Function gatt_check_enc_req +** +** Description check link security. +** +** Returns TRUE if encrypted, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_SEC_ACTION gatt_sec_act; + tBTM_BLE_SEC_ACT btm_ble_sec_act; + BOOLEAN status = TRUE; + tBTM_STATUS btm_status; + + if ( gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) + { + gatt_sec_act = gatt_determine_sec_act(p_clcb); + gatt_set_sec_act(p_tcb, gatt_sec_act); + switch (gatt_sec_act ) + { + case GATT_SEC_SIGN_DATA: + GATT_TRACE_DEBUG0("gatt_security_check_start: Do data signing"); + gatt_set_ch_state(p_tcb, GATT_CH_W4_DATA_SIGN_COMP); + gatt_sign_data(p_clcb); + break; + case GATT_SEC_ENCRYPT: + case GATT_SEC_ENCRYPT_NO_MITM: + case GATT_SEC_ENCRYPT_MITM: + GATT_TRACE_DEBUG0("gatt_security_check_start: Encrypt now or key upgreade first"); + gatt_convert_sec_action(p_tcb->sec_act, &btm_ble_sec_act); + gatt_set_ch_state(p_tcb, GATT_CH_W4_SEC_COMP); + p_tcb->p_clcb = p_clcb; /* keep the clcb pointer in CCB */ + btm_status = BTM_SetEncryption(p_tcb->peer_bda, gatt_enc_cmpl_cback, &btm_ble_sec_act); + if ( (btm_status != BTM_SUCCESS) && (btm_status != BTM_CMD_STARTED)) + { + GATT_TRACE_ERROR1("gatt_security_check_start BTM_SetEncryption failed btm_status=%d", btm_status); + p_tcb->p_clcb = NULL; + status = FALSE; + } + break; + default: + gatt_sec_check_complete(TRUE, p_clcb); + break; + } + + if (status == FALSE) + { + gatt_set_sec_act(p_tcb, GATT_SEC_NONE); + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + } + } + else + { + GATT_TRACE_ERROR0("gatt_security_check_start channel not open"); + status = FALSE; + } + + return status; +} + + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_cl.c b/stack/gatt/gatt_cl.c new file mode 100644 index 0000000..91fb985 --- /dev/null +++ b/stack/gatt/gatt_cl.c @@ -0,0 +1,1220 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT client functions + * + ******************************************************************************/ + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include +#include "gki.h" +#include "gatt_int.h" + +#define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */ +#define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80) +#define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90) + +/******************************************************************************** +** G L O B A L G A T T D A T A * +*********************************************************************************/ +void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb); + +UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = +{ + 0, + GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */ + GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */ + GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */ +}; + +UINT16 disc_type_to_uuid[GATT_DISC_MAX] = +{ + 0, /* reserved */ + GATT_UUID_PRI_SERVICE, /* DISC_SRVC_ALL */ + GATT_UUID_PRI_SERVICE, /* for DISC_SERVC_BY_UUID */ + GATT_UUID_INCLUDE_SERVICE, /* for DISC_INC_SRVC */ + GATT_UUID_CHAR_DECLARE, /* for DISC_CHAR */ + 0 /* no type filtering for DISC_CHAR_DSCPT */ +}; + + +/******************************************************************************* +** +** Function gatt_act_discovery +** +** Description GATT discovery operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_discovery(tGATT_CLCB *p_clcb) +{ + UINT8 op_code = disc_type_to_att_opcode[p_clcb->op_subtype]; + tGATT_CL_MSG cl_req; + + if (p_clcb->s_handle <= p_clcb->e_handle && p_clcb->s_handle != 0) + { + memset(&cl_req, 0, sizeof(tGATT_CL_MSG)); + + cl_req.browse.s_handle = p_clcb->s_handle; + cl_req.browse.e_handle = p_clcb->e_handle; + + if (disc_type_to_uuid[p_clcb->op_subtype] != 0) + { + cl_req.browse.uuid.len = 2; + cl_req.browse.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + } + + if (p_clcb->op_subtype == GATT_DISC_SRVC_BY_UUID) /* fill in the FindByTypeValue request info*/ + { + cl_req.find_type_value.uuid.len = 2; + cl_req.find_type_value.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + cl_req.find_type_value.s_handle = p_clcb->s_handle; + cl_req.find_type_value.e_handle = p_clcb->e_handle; + cl_req.find_type_value.value_len = p_clcb->uuid.len; + memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len); + } + + if (attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req) != GATT_SUCCESS) + { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + } + else /* end of handle range */ + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); +} + +/******************************************************************************* +** +** Function gatt_act_read +** +** Description GATT read operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + UINT8 rt = GATT_INTERNAL_ERROR; + tGATT_CL_MSG msg; + UINT8 op_code = 0; + + memset (&msg, 0, sizeof(tGATT_CL_MSG)); + + switch (p_clcb->op_subtype) + { + case GATT_READ_CHAR_VALUE: + case GATT_READ_BY_TYPE: + op_code = GATT_REQ_READ_BY_TYPE; + msg.browse.s_handle = p_clcb->s_handle; + msg.browse.e_handle = p_clcb->e_handle; + if (p_clcb->op_subtype == GATT_READ_BY_TYPE) + memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); + else + { + msg.browse.uuid.len = LEN_UUID_16; + msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE; + } + break; + + case GATT_READ_CHAR_VALUE_HDL: + case GATT_READ_BY_HANDLE: + if (!p_clcb->counter) + { + op_code = GATT_REQ_READ; + msg.handle = p_clcb->s_handle; + } + else + { + if (!p_clcb->first_read_blob_after_read) + p_clcb->first_read_blob_after_read = TRUE; + else + p_clcb->first_read_blob_after_read = FALSE; + + GATT_TRACE_DEBUG1("gatt_act_read first_read_blob_after_read=%d", + p_clcb->first_read_blob_after_read); + op_code = GATT_REQ_READ_BLOB; + msg.read_blob.offset = offset; + msg.read_blob.handle = p_clcb->s_handle; + } + p_clcb->op_subtype &= ~ 0x80; + break; + + case GATT_READ_PARTIAL: + op_code = GATT_REQ_READ_BLOB; + msg.read_blob.handle = p_clcb->s_handle; + msg.read_blob.offset = offset; + break; + + case GATT_READ_MULTIPLE: + op_code = GATT_REQ_READ_MULTI; + memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI)); + break; + + case GATT_READ_INC_SRV_UUID128: + op_code = GATT_REQ_READ; + msg.handle = p_clcb->s_handle; + p_clcb->op_subtype &= ~ 0x90; + break; + + default: + GATT_TRACE_ERROR1("Unknown read type: %d", p_clcb->op_subtype); + break; + } + + if ( op_code == 0 || + (rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg)) != GATT_SUCCESS) + { + gatt_end_operation(p_clcb, rt, NULL); + } +} + +/******************************************************************************* +** +** Function gatt_act_write +** +** Description GATT write operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_write (tGATT_CLCB *p_clcb) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + UINT8 rt = GATT_SUCCESS, op_code; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + if (p_attr) + { + switch (p_clcb->op_subtype) + { + case GATT_WRITE_NO_RSP: + p_clcb->s_handle = p_attr->handle; + op_code = (p_tcb->sec_act & GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE; + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + op_code, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + break; + + case GATT_WRITE: + if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) + { + p_clcb->s_handle = p_attr->handle; + + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + GATT_REQ_WRITE, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + } + else /* prepare write for long attribute */ + { + gatt_send_prepare_write(p_tcb, p_clcb); + } + break; + + case GATT_WRITE_PREPARE: + gatt_send_prepare_write(p_tcb, p_clcb); + break; + + default: + rt = GATT_INTERNAL_ERROR; + GATT_TRACE_ERROR1("Unknown write type: %d", p_clcb->op_subtype); + break; + } + } + else + rt = GATT_INTERNAL_ERROR; + + if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED) + || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) + { + if (rt != GATT_SUCCESS) + { + GATT_TRACE_ERROR1("gatt_act_write() failed op_code=0x%x", op_code); + } + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_send_queue_write_cancel +** +** Description send queue write cancel +** +** Returns void. +** +*******************************************************************************/ +void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag) +{ + UINT8 rt ; + + GATT_TRACE_DEBUG0("gatt_send_queue_write_cancel "); + + rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag); + + if (rt != GATT_SUCCESS) + { + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_check_write_long_terminate +** +** Description To terminate write long or not. +** +** Returns TRUE: write long is terminated; FALSE keep sending. +** +*******************************************************************************/ +BOOLEAN gatt_check_write_long_terminate(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_VALUE *p_rsp_value) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + BOOLEAN exec = FALSE; + tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC; + + GATT_TRACE_DEBUG0("gatt_check_write_long_terminate "); + /* check the first write response status */ + if (p_rsp_value != NULL) + { + if (p_rsp_value->handle != p_attr->handle || + p_rsp_value->len != p_clcb->counter || + memcmp(p_rsp_value->value, p_attr->value + p_attr->offset, p_rsp_value->len)) + { + /* data does not match */ + p_clcb->status = GATT_ERROR; + flag = GATT_PREP_WRITE_CANCEL; + exec = TRUE; + } + else /* response checking is good */ + { + p_clcb->status = GATT_SUCCESS; + /* update write offset and check if end of attribute value */ + if ((p_attr->offset += p_rsp_value->len) >= p_attr->len) + exec = TRUE; + } + } + if (exec) + { + gatt_send_queue_write_cancel (p_tcb, p_clcb, flag); + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** Function gatt_send_prepare_write +** +** Description Send prepare write. +** +** Returns void. +** +*******************************************************************************/ +void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + UINT16 to_send, offset; + UINT8 rt = GATT_SUCCESS; + UINT8 type = p_clcb->op_subtype; + + GATT_TRACE_DEBUG1("gatt_send_prepare_write type=0x%x", type ); + to_send = p_attr->len - p_attr->offset; + + if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) /* 2 = UINT16 offset bytes */ + to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE; + + p_clcb->s_handle = p_attr->handle; + + offset = p_attr->offset; + if (type == GATT_WRITE_PREPARE) + { + offset += p_clcb->start_offset; + } + + GATT_TRACE_DEBUG2("offset =0x%x len=%d", offset, to_send ); + + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + GATT_REQ_PREPARE_WRITE, + p_attr->handle, + to_send, /* length */ + offset, /* used as offset */ + p_attr->value + p_attr->offset); /* data */ + + /* remember the write long attribute length */ + p_clcb->counter = to_send; + + if (rt != GATT_SUCCESS ) + { + gatt_end_operation(p_clcb, rt, NULL); + } +} + +/******************************************************************************* +** +** Function gatt_proc_disc_read_by_type_rsp +** +** Description This function process the read by type response and send another +** request if needed. +** +** Returns void. +** +*******************************************************************************/ +void gatt_proc_disc_read_by_type_rsp(tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + /* + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_DISCOVERY_DB *p_db = p_clcb->p_disc_db; + tGATT_DISC_REC *p_rec; + tGATT_STATUS status = GATT_INTERNAL_ERROR; + + + if ((p_rec = gatt_add_record(p_clcb->p_disc_db)) != NULL) + { + p_rec->handle = handle; + p_rec->type = p_db->uuid_filter; + p_rec->attr_len = len; + + // copy the attibute value into DB + p_rec->p_value = p_db->p_free_mem; + memcpy(p_rec->p_value, p_value, len); + p_db->p_free_mem += len; + p_db->mem_free -= len; + + if (handle < p_clcb->e_handle) + { + // send another request + if (gatt_act_send_browse(p_tcb, p_clcb->conn_id, + GATT_REQ_READ_BY_TYPE, + (UINT16)(handle + 1), // starting handle + p_clcb->e_handle, // end handle + p_clcb->p_disc_db->uuid_filter) // uuid filter / + == GATT_SUCCESS) + { + status = GATT_SUCCESS; + } + } + } + else + status = GATT_DB_FULL; + + if (status != GATT_SUCCESS) // DB full + { + gatt_end_operation(p_clcb, status, NULL); + }*/ +} +/******************************************************************************* +** +** Function gatt_process_find_type_value_rsp +** +** Description This function is called to handle find by type value response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + tGATT_DISC_VALUE record_value; + UINT8 *p = p_data; + + GATT_TRACE_DEBUG0("gatt_process_find_type_value_rsp "); + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) + return; + + memset (&record_value, 0, sizeof(tGATT_DISC_VALUE)); + result.type.len = 2; + result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE; + + /* returns a series of handle ranges */ + while (len >= 4) + { + STREAM_TO_UINT16 (result.handle, p); + STREAM_TO_UINT16 (record_value.handle, p); + len -= 4; + + memcpy (&result.value, &record_value, sizeof (result.value));; + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + + /* last handle + 1 */ + p_clcb->s_handle = (record_value.handle == 0) ? 0 : (record_value.handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_process_read_info_rsp +** +** Description This function is called to handle the read information +** response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + UINT8 *p = p_data, uuid_len = 0, type; + + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT) + return; + + STREAM_TO_UINT8(type, p); + len -= 1; + + if (type == GATT_INFO_TYPE_PAIR_16) + uuid_len = LEN_UUID_16; + else if (type == GATT_INFO_TYPE_PAIR_128) + uuid_len = LEN_UUID_128; + + while (len >= uuid_len + 2) + { + STREAM_TO_UINT16 (result.handle, p); + + if (uuid_len > 0) + { + if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) + break; + } + else + memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID)); + + len -= (uuid_len + 2); + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + + p_clcb->s_handle = (result.handle == 0) ? 0 :(result.handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_proc_disc_error_rsp +** +** Description This function process the read by type response and send another +** request if needed. +** +** Returns void. +** +*******************************************************************************/ +void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode, + UINT16 handle, UINT8 reason) +{ + tGATT_STATUS status = (tGATT_STATUS) reason; + + GATT_TRACE_DEBUG2("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode); + + switch (opcode) + { + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_FIND_INFO: + if (reason == GATT_NOT_FOUND) + { + status = GATT_SUCCESS; + GATT_TRACE_DEBUG0("Discovery completed"); + } + break; + default: + GATT_TRACE_ERROR1("Incorrect discovery opcode %04x", opcode); + break; + } + + gatt_end_operation(p_clcb, status, NULL); +} + +/******************************************************************************* +** +** Function gatt_process_error_rsp +** +** Description This function is called to handle the error response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT8 opcode, reason, * p= p_data; + UINT16 handle; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + GATT_TRACE_DEBUG0("gatt_process_error_rsp "); + STREAM_TO_UINT8(opcode, p); + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT8(reason, p); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) + { + gatt_proc_disc_error_rsp(p_tcb, p_clcb, opcode, handle, reason); + } + else + { + if ( (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE) && + (opcode == GATT_REQ_PREPARE_WRITE) && + (p_attr) && + (handle == p_attr->handle) ) + { + p_clcb->status = reason; + gatt_send_queue_write_cancel(p_tcb, p_clcb, GATT_PREP_WRITE_CANCEL); + } + else if ((p_clcb->operation == GATTC_OPTYPE_READ) && + ((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) || + (p_clcb->op_subtype == GATT_READ_BY_HANDLE)) && + (opcode == GATT_REQ_READ_BLOB) && + p_clcb->first_read_blob_after_read && + (reason == GATT_NOT_LONG)) + { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } + else + gatt_end_operation(p_clcb, reason, NULL); + } +} +/******************************************************************************* +** +** Function gatt_process_prep_write_rsp +** +** Description This function is called to handle the read response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + UINT8 *p= p_data; + + GATT_TRACE_ERROR2("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); + + STREAM_TO_UINT16 (value.handle, p); + STREAM_TO_UINT16 (value.offset, p); + + value.len = len - 4; + + memcpy (value.value, p, value.len); + + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) + { + p_clcb->status = GATT_SUCCESS; + /* application should verify handle offset + and value are matched or not */ + + gatt_end_operation(p_clcb, p_clcb->status, &value); + } + else if (p_clcb->op_subtype == GATT_WRITE ) + { + if (!gatt_check_write_long_terminate(p_tcb, p_clcb, &value)) + gatt_send_prepare_write(p_tcb, p_clcb); + } + +} +/******************************************************************************* +** +** Function gatt_process_notification +** +** Description This function is called to handle the handle value indication +** or handle value notification. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + tGATT_REG *p_reg; + UINT16 conn_id; + tGATT_STATUS encrypt_status; + UINT8 *p= p_data, i, + event = (op_code == GATT_HANDLE_VALUE_NOTIF) ? GATTC_OPTYPE_NOTIFICATION : GATTC_OPTYPE_INDICATION; + + GATT_TRACE_DEBUG0("gatt_process_notification "); + + STREAM_TO_UINT16 (value.handle, p); + value.len = len - 2; + memcpy (value.value, p, value.len); + + if (!GATT_HANDLE_IS_VALID(value.handle)) + { + /* illegal handle, send ack now */ + if (op_code == GATT_HANDLE_VALUE_IND) + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + return; + } + + if (event == GATTC_OPTYPE_INDICATION) + { + if (p_tcb->ind_count) + { + /* this is an error case that receiving an indication but we + still has an indication not being acked yet. + For now, just log the error reset the counter. + Later we need to disconnect the link unconditionally. + */ + GATT_TRACE_ERROR1("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count); + } + p_tcb->ind_count = 0; + } + + /* should notify all registered client with the handle value notificaion/indication + Note: need to do the indication count and start timer first then do callback + */ + + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) + { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION)) + p_tcb->ind_count++; + } + + if (event == GATTC_OPTYPE_INDICATION) + { + /* start a timer for app confirmation */ + if (p_tcb->ind_count > 0) + gatt_start_ind_ack_timer(p_tcb); + else /* no app to indicate, or invalid handle */ + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + } + + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) + { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) + { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + encrypt_status = gatt_get_link_encrypt_status(p_tcb); + (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); + } + } + +} + +/******************************************************************************* +** +** Function gatt_process_read_by_type_rsp +** +** Description This function is called to handle the read by type response. +** read by type can be used for discovery, or read by type or +** read characteristic value. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + tGATT_DISC_VALUE record_value; + UINT8 *p = p_data, value_len, handle_len = 2; + UINT16 handle = 0; + + /* discovery procedure and no callback function registered */ + if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) + return; + + STREAM_TO_UINT8(value_len, p); + + if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len-1)) ) + { + /* this is an error case that server's response containing a value length which is larger than MTU-2 + or value_len > message total length -1 */ + GATT_TRACE_ERROR4("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)", + op_code, value_len, (p_tcb->payload_size - 2), (len-1)); + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + return; + } + + if (op_code == GATT_RSP_READ_BY_GRP_TYPE) + handle_len = 4; + + value_len -= handle_len; /* substract the handle pairs bytes */ + len -= 1; + + while (len >= (handle_len + value_len)) + { + STREAM_TO_UINT16(handle, p); + + if (!GATT_HANDLE_IS_VALID(handle)) + { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + memset(&result, 0, sizeof(tGATT_DISC_RES)); + memset(&record_value, 0, sizeof(tGATT_DISC_VALUE)); + + result.handle = handle; + result.type.len = 2; + result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + + /* discover all services */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL && + op_code == GATT_RSP_READ_BY_GRP_TYPE) + { + STREAM_TO_UINT16(handle, p); + + if (!GATT_HANDLE_IS_VALID(handle)) + { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + else + { + record_value.group_value.e_handle = handle; + if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) + { + GATT_TRACE_ERROR0("discover all service response parsing failure"); + break; + } + } + } + /* discover included service */ + else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) + { + STREAM_TO_UINT16(record_value.incl_service.s_handle, p); + STREAM_TO_UINT16(record_value.incl_service.e_handle, p); + + if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) || + !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle)) + { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + if(value_len == 6) + { + STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p); + record_value.incl_service.service_type.len = LEN_UUID_16; + } + else if (value_len == 4) + { + p_clcb->s_handle = record_value.incl_service.s_handle; + p_clcb->read_uuid128.wait_for_read_rsp = TRUE; + p_clcb->read_uuid128.next_disc_start_hdl = handle + 1; + memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result)); + memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value)); + p_clcb->op_subtype |= 0x90; + gatt_act_read(p_clcb, 0); + return; + } + else + { + GATT_TRACE_ERROR1("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + return; + } + } + /* read by type */ + else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE) + { + p_clcb->counter = len - 2; + p_clcb->s_handle = handle; + if ( p_clcb->counter == (p_clcb->p_tcb->payload_size -4)) + { + p_clcb->op_subtype = GATT_READ_BY_HANDLE; + if (!p_clcb->p_attr_buf) + p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN); + if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN) + { + memcpy(p_clcb->p_attr_buf, p, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } + else + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p); + } + else + { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } + return; + } + else /* discover characterisitic or read characteristic value */ + { + STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); + STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); + if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) + { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p); + + /* UUID not matching */ + if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid)) + { + len -= (value_len + 2); + continue; /* skip the result, and look for next one */ + } + else if (p_clcb->operation == GATTC_OPTYPE_READ) + /* UUID match for read characteristic value */ + { + /* only read the first matching UUID characteristic value, and + discard the rest results */ + p_clcb->s_handle = record_value.dclr_value.val_handle; + p_clcb->op_subtype |= 0x80; + gatt_act_read(p_clcb, 0); + return; + } + } + len -= (value_len + handle_len); + + /* result is (handle, 16bits UUID) pairs */ + memcpy (&result.value, &record_value, sizeof (result.value)); + + /* send callback if is discover procedure */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb) + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + + p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) + { + /* initiate another request */ + gatt_act_discovery(p_clcb) ; + } + else /* read characteristic value */ + { + gatt_act_read(p_clcb, 0); + } +} + +/******************************************************************************* +** +** Function gatt_process_read_rsp +** +** Description This function is called to handle the read BLOB response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 offset = p_clcb->counter; + UINT8 * p= p_data; + + if (p_clcb->operation == GATTC_OPTYPE_READ) + { + if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) + { + p_clcb->counter = len; + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } + else + { + + /* allocate GKI buffer holding up long attribute value */ + if (!p_clcb->p_attr_buf) + p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN); + + /* copy attrobute value into cb buffer */ + if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN) + { + if ((len + offset) > GATT_MAX_ATTR_LEN) + len = GATT_MAX_ATTR_LEN - offset; + + p_clcb->counter += len; + + memcpy(p_clcb->p_attr_buf + offset, p, len); + + /* send next request if needed */ + + if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ + len + offset < GATT_MAX_ATTR_LEN) + { + GATT_TRACE_DEBUG3("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", + offset, len, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } + else /* end of request, send callback */ + { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } + } + else /* exception, should not happen */ + { + GATT_TRACE_ERROR2("attr offset = %d p_attr_buf = %d ", offset, p_clcb->p_attr_buf); + gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf); + } + } + } + else + { + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_INC_SRVC && + p_clcb->read_uuid128.wait_for_read_rsp ) + { + p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl; + p_clcb->read_uuid128.wait_for_read_rsp = FALSE; + if (len == LEN_UUID_128) + { + + memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len); + p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128; + if ( p_clcb->p_reg->app_cb.p_disc_res_cb) + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result); + gatt_act_discovery(p_clcb) ; + } + else + { + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + } + } + } + +} + + +/******************************************************************************* +** +** Function gatt_process_handle_rsp +** +** Description This function is called to handle the write response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_handle_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT16 handle; + UINT8 * p= p_data; + + STREAM_TO_UINT16(handle, p); + len -= 2; + + if (op_code == GATT_RSP_WRITE) + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); +} +/******************************************************************************* +** +** Function gatt_process_mtu_rsp +** +** Description This function is called to process the configure MTU response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu; + + STREAM_TO_UINT16(mtu, p_data); + + if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE) + p_tcb->payload_size = mtu; + + gatt_end_operation(p_clcb, p_clcb->status, NULL); +} +/******************************************************************************* +** +** Function gatt_cmd_to_rsp_code +** +** Description The function convert a ATT command op code into the corresponding +** response code assume no error occurs. +** +** Returns response code. +** +*******************************************************************************/ +UINT8 gatt_cmd_to_rsp_code (UINT8 cmd_code) +{ + UINT8 rsp_code = 0; + + if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) + { + rsp_code = cmd_code + 1; + } + return rsp_code; +} +/******************************************************************************* +** +** Function gatt_cl_send_next_cmd_inq +** +** Description Find next command in queue and sent to server +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + BOOLEAN sent = FALSE; + + while (!sent && + p_tcb->pending_cl_req != p_tcb->next_slot_inq && + p_cmd->to_send && p_cmd->p_cmd != NULL) + { + sent = attp_send_msg_to_L2CAP(p_tcb, p_cmd->p_cmd); + + if (sent) + { + p_cmd->to_send = FALSE; + p_cmd->p_cmd = NULL; + + gatt_start_rsp_timer (p_tcb); + } + else + { + GATT_TRACE_ERROR0("gatt_cl_send_next_cmd_inq: L2CAP sent error"); + + memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); + p_tcb->pending_cl_req ++; + p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + } + } + return sent; +} + +/******************************************************************************* +** +** Function gatt_client_handle_server_rsp +** +** Description This function is called to handle the server response to +** client. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_CLCB *p_clcb = NULL; + UINT8 rsp_code; + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF) + { + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); + + rsp_code = gatt_cmd_to_rsp_code(rsp_code); + + if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) + { + GATT_TRACE_WARNING2 ("ATT - Ignore wrong response. Receives (%02x) \ + Request(%02x) Ignored", op_code, rsp_code); + + return; + } + else + btu_stop_timer (&p_tcb->rsp_timer_ent); + } + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not count op_code */ + if (len >= p_tcb->payload_size) + { + GATT_TRACE_ERROR2("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size); + if (op_code != GATT_HANDLE_VALUE_NOTIF && + op_code != GATT_HANDLE_VALUE_IND) + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + else + { + switch (op_code) + { + case GATT_RSP_ERROR: + gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_MTU: /* 2 bytes mtu */ + gatt_process_mtu_rsp(p_tcb, p_clcb, len ,p_data); + break; + + case GATT_RSP_FIND_INFO: + gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ_BY_GRP_TYPE: + gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ: + case GATT_RSP_READ_BLOB: + case GATT_RSP_READ_MULTI: + gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */ + gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data); + break; + + case GATT_RSP_WRITE: + gatt_process_handle_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_PREPARE_WRITE: + gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_EXEC_WRITE: + gatt_end_operation(p_clcb, p_clcb->status, NULL); + break; + + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + gatt_process_notification(p_tcb, op_code, len, p_data); + break; + + default: + GATT_TRACE_ERROR1("Unknown opcode = %d", op_code); + break; + } + } + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF) + { + gatt_cl_send_next_cmd_inq(p_tcb); + } + + return; +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_db.c b/stack/gatt/gatt_db.c new file mode 100644 index 0000000..bd94e50 --- /dev/null +++ b/stack/gatt/gatt_db.c @@ -0,0 +1,1130 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT database building and query functions + * + ******************************************************************************/ + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include "bt_trace.h" + +#include +#include +#include "gatt_int.h" +#include "l2c_api.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 BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db); +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, UINT16 uuid16, UINT8 *p_uuid128, tGATT_PERM perm); +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr); +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len); + +static void gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID service, BOOLEAN is_pri); +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id); + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID service, BOOLEAN is_pri, + UINT16 s_hdl, UINT16 num_handle) +{ + if (!allocate_svc_db_buf(p_db)) + { + GATT_TRACE_ERROR0("gatts_init_service_db failed, no resources"); + return FALSE; + } + + GATT_TRACE_DEBUG0("gatts_init_service_db"); + GATT_TRACE_DEBUG2("s_hdl = %d num_handle = %d", s_hdl, num_handle ); + + /* update service database information */ + p_db->next_handle = s_hdl; + p_db->end_handle = s_hdl + num_handle; + + gatts_db_add_service_declaration(p_db, service, is_pri); + + return TRUE; +} + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +tBT_UUID * gatts_get_service_uuid (tGATT_SVC_DB *p_db) +{ + if (!p_db || !p_db->p_attr_list) + { + GATT_TRACE_ERROR0("service DB empty"); + + return NULL; + } + else + { + return &((tGATT_ATTR16 *)p_db->p_attr_list)->p_value->uuid; + } +} + +/******************************************************************************* +** +** Function gatts_check_attr_readability +** +** Description check attribute readability +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, + UINT16 offset, + BOOLEAN read_long, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 min_key_size; + tGATT_PERM perm = p_attr->permission; + + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) + { + min_key_size +=6; + } + + if (!(perm & GATT_READ_ALLOWED)) + { + GATT_TRACE_ERROR0( "GATT_READ_NOT_PERMIT"); + return GATT_READ_NOT_PERMIT; + } + + if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED)) + { + GATT_TRACE_ERROR0( "GATT_INSUF_AUTHENTICATION"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) + { + GATT_TRACE_ERROR0( "GATT_INSUF_AUTHENTICATION: MITM Required"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) + { + GATT_TRACE_ERROR0( "GATT_INSUF_ENCRYPTION"); + return GATT_INSUF_ENCRYPTION; + } + + if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) + { + GATT_TRACE_ERROR0( "GATT_INSUF_KEY_SIZE"); + return GATT_INSUF_KEY_SIZE; + } + + + if (read_long) + { + switch (p_attr->uuid) + { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + case GATT_UUID_CHAR_EXT_PROP: + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + case GATT_UUID_CHAR_PRESENT_FORMAT: + GATT_TRACE_ERROR0("GATT_NOT_LONG"); + return GATT_NOT_LONG; + + default: + break; + } + } + + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function read_attr_value +** +** Description Utility function to read an attribute value. +** +** Parameter p_attr: pointer to the attribute to read. +** offset: read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter to carry out the attribute length. +** read_long: this is a read blob request. +** mtu: MTU +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS read_attr_value (void *p_attr, + UINT16 offset, + UINT8 **p_data, + BOOLEAN read_long, + UINT16 mtu, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 len = 0, uuid16 = 0; + UINT8 *p = *p_data; + tGATT_STATUS status; + UINT16 read_long_uuid=0; + tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; + + GATT_TRACE_DEBUG5("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d", + p_attr16->uuid, + p_attr16->permission, + sec_flag, + offset, + read_long); + + status = gatts_check_attr_readability((tGATT_ATTR16 *)p_attr, offset, read_long, sec_flag, key_size); + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) + uuid16 = p_attr16->uuid; + + if (status != GATT_SUCCESS) + return status; + + status = GATT_NO_RESOURCES; + + if (read_long && + (uuid16 == GATT_UUID_CHAR_DESCRIPTION || uuid16 == GATT_UUID_CHAR_AGG_FORMAT)) + { + read_long_uuid = p_attr16->uuid; + } + + if (uuid16 == GATT_UUID_PRI_SERVICE || uuid16 == GATT_UUID_SEC_SERVICE) + { + len = p_attr16->p_value->uuid.len; + if (mtu >= p_attr16->p_value->uuid.len) + { + gatt_build_uuid_to_stream(&p, p_attr16->p_value->uuid); + status = GATT_SUCCESS; + } + } + else if (uuid16 == GATT_UUID_CHAR_DECLARE) + { + len = (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) ? 5 :19; + + if (mtu >= len) + { + UINT8_TO_STREAM(p, p_attr16->p_value->char_decl.property); + UINT16_TO_STREAM(p, p_attr16->p_value->char_decl.char_val_handle); + + if (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) + { + UINT16_TO_STREAM(p, ((tGATT_ATTR16 *)(p_attr16->p_next))->uuid); + } + else + { + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *)(p_attr16->p_next))->uuid, LEN_UUID_128); + } + status = GATT_SUCCESS; + } + + } + else if (uuid16 == GATT_UUID_INCLUDE_SERVICE) + { + len = (p_attr16->p_value->incl_handle.service_type.len == 2) ? 6 : 4; + if (mtu >= len) + { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.s_handle); + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.e_handle); + + if (p_attr16->p_value->incl_handle.service_type.len == 2) + { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.service_type.uu.uuid16); + } + status = GATT_SUCCESS; + } + } + else /* characteristic description or characteristic value */ + { + status = GATT_PENDING; + } + + *p_len = len; + *p_data = p; + return status; +} + +/******************************************************************************* +** +** Function gatts_db_read_attr_value_by_type +** +** Description Query attribute value by attribute type. +** +** Parameter p_db: pointer to the attribute database. +** p_rsp: Read By type response data. +** s_handle: starting handle of the range we are looking for. +** e_handle: ending handle of the range we are looking for. +** type: Attribute type. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + BT_HDR *p_rsp, + UINT16 s_handle, + UINT16 e_handle, + tBT_UUID type, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id, + UINT16 *p_cur_handle) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 len = 0; + UINT8 *p = (UINT8 *)(p_rsp + 1) + p_rsp->len + L2CAP_MIN_OFFSET; + tBT_UUID attr_uuid; + + if (p_db && p_db->p_attr_list) + { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && p_attr->handle <= e_handle) + { + if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) + { + attr_uuid.len = LEN_UUID_16; + attr_uuid.uu.uuid16 = p_attr->uuid; + } + else + { + attr_uuid.len = LEN_UUID_128; + memcpy(attr_uuid.uu.uuid128, ((tGATT_ATTR128 *)p_attr)->uuid, LEN_UUID_128); + } + + if (p_attr->handle >= s_handle && gatt_uuid_compare(type, attr_uuid)) + { + if (*p_len <= 2) + { + status = GATT_NO_RESOURCES; + break; + } + + UINT16_TO_STREAM (p, p_attr->handle); + + status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len -2), &len, sec_flag, key_size); + + if (status == GATT_PENDING) + { + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id); + + /* one callback at a time */ + break; + } + else if (status == GATT_SUCCESS) + { + if (p_rsp->offset == 0) + p_rsp->offset = len + 2; + + if (p_rsp->offset == len + 2) + { + p_rsp->len += (len + 2); + *p_len -= (len + 2); + } + else + { + GATT_TRACE_ERROR0("format mismatch"); + status = GATT_NO_RESOURCES; + break; + } + } + else + { + *p_cur_handle = p_attr->handle; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_add_included_service +** +** Description This function adds an included service into a database. +** +** Parameter p_db: database pointer. +** inc_srvc_type: included service type. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, + tBT_UUID service) +{ + tGATT_ATTR16 *p_attr; + + GATT_TRACE_DEBUG3("gatts_add_included_service: s_hdl = 0x%04x e_hdl = 0x%04x uuid = 0x%04x", + s_handle, e_handle, service.uu.uuid16); + + if (service.len == 0 || s_handle == 0 || e_handle == 0) + { + GATT_TRACE_ERROR0("gatts_add_included_service Illegal Params."); + return 0; + } + + if ((p_attr = (tGATT_ATTR16 *) allocate_attr_in_db(p_db, GATT_UUID_INCLUDE_SERVICE, NULL, GATT_PERM_READ)) != NULL) + { + if (copy_extra_byte_in_db(p_db, (void **)&p_attr->p_value, sizeof(tGATT_INCL_SRVC))) + { + p_attr->p_value->incl_handle.s_handle = s_handle; + p_attr->p_value->incl_handle.e_handle = e_handle; + memcpy(&p_attr->p_value->incl_handle.service_type, &service, sizeof(tBT_UUID)); + + return p_attr->handle; + } + else + { + deallocate_attr_in_db(p_db, p_attr); + } + } + + return 0; +} + +/******************************************************************************* +** +** Function gatts_add_characteristic +** +** Description This function add a characteristics and its descriptor into +** a servce identified by the service database pointer. +** +** Parameter p_db: database pointer. +** perm: permission (authentication and key size requirements) +** property: property of the characteristic. +** p_char: characteristic value information. +** +** Returns Status of te operation. +** +*******************************************************************************/ +UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID * p_char_uuid) +{ + tGATT_ATTR16 *p_char_decl, *p_char_val; + UINT16 uuid16 = (p_char_uuid->len == LEN_UUID_16) ? p_char_uuid->uu.uuid16 : 0; + + GATT_TRACE_DEBUG2("gatts_add_characteristic perm=0x%0x property=0x%0x", perm, property); + + if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, GATT_UUID_CHAR_DECLARE, NULL, GATT_PERM_READ)) != NULL) + { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) + { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_val = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, uuid16, p_char_uuid->uu.uuid128, perm); + + if (p_char_val == NULL) + { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_decl->p_value->char_decl.property = property; + p_char_decl->p_value->char_decl.char_val_handle = p_char_val->handle; + + p_char_val->p_value = NULL; + + return p_char_val->handle; + } + + return 0; +} + +/******************************************************************************* +** +** Function gatt_convertchar_descr_type +** +** Description This function convert a char descript UUID into descriptor type. +** +** Returns descriptor type. +** +*******************************************************************************/ +UINT8 gatt_convertchar_descr_type(tBT_UUID *p_descr_uuid) +{ + tBT_UUID std_descr = {LEN_UUID_16, {GATT_UUID_CHAR_EXT_PROP}}; + + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_EXT_DSCPTOR; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_USER_DSCPTOR; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_CLT_CONFIG; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_SVR_CONFIG; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_PRES_FORMAT; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_AGGR_FORMAT; + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) + return GATT_DESCR_VALID_RANGE; + + + return GATT_DESCR_UNKNOWN; +} + +/******************************************************************************* +** +** Function gatts_add_char_descr +** +** Description This function add a characteristics descriptor. +** +** Parameter p_db: database pointer. +** perm: characteristic descriptor permission type. +** char_dscp_tpye: the characteristic descriptor masks. +** p_dscp_params: characteristic descriptors values. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID * p_descr_uuid) +{ + tGATT_ATTR16 *p_char_dscptr; + UINT16 uuid16 = (p_descr_uuid->len == LEN_UUID_16)? p_descr_uuid->uu.uuid16 : 0; + + GATT_TRACE_DEBUG1("gatts_add_char_descr uuid=0x%04x", p_descr_uuid->uu.uuid16); + + /* Add characteristic descriptors */ + if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, + uuid16, + p_descr_uuid->uu.uuid128, + perm)) + == NULL) + { + GATT_TRACE_DEBUG0("gatts_add_char_descr Fail for adding char descriptors."); + return 0; + } + else + { + return p_char_dscptr->handle; + } +} + +/*******************************************************************************/ +/* Service Attribute Database Query Utility Functions */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function gatts_read_attr_value_by_handle +** +** Description Query attribute value by attribute handle. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, + UINT16 mtu, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT8 *pp = p_value; + + if (p_db && p_db->p_attr_list) + { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) + { + if (p_attr->handle == handle) + { + status = read_attr_value (p_attr, offset, &pp, + (BOOLEAN)(op_code == GATT_REQ_READ_BLOB), + mtu, p_len, sec_flag, key_size); + + if (status == GATT_PENDING) + { + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id); + } + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_read_attr_perm_check +** +** Description Check attribute readability. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, + BOOLEAN is_long, + UINT16 handle, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) + { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) + { + if (p_attr->handle == handle) + { + status = gatts_check_attr_readability (p_attr, 0, + is_long, + sec_flag, key_size); + break; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + + return status; +} +/******************************************************************************* +** +** Function gatts_write_attr_perm_check +** +** Description Write attribute value into database. +** +** Parameter p_db: pointer to the attribute database. +** op_code:op code of this write. +** handle: handle of the attribute to write. +** offset: Write offset if write op code is write blob. +** p_data: Attribute value to write. +** len: attribute data length. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 max_size = 0; + tGATT_PERM perm; + UINT16 min_key_size; + + GATT_TRACE_DEBUG6( "gatts_write_attr_perm_check op_code=0x%0x handle=0x%04x offset=%d len=%d sec_flag=0x%0x key_size=%d", + op_code, handle, offset, len, sec_flag, key_size); + + if (p_db != NULL) + { + p_attr = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_attr != NULL) + { + if (p_attr->handle == handle) + { + perm = p_attr->permission; + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) + { + min_key_size +=6; + } + GATT_TRACE_DEBUG2( "gatts_write_attr_perm_check p_attr->permission =0x%04x min_key_size==0x%04x", + p_attr->permission, + min_key_size); + + if ((op_code == GATT_CMD_WRITE) && (perm & GATT_WRITE_SIGNED_PERM) ) + { + /* use the rules for the mixed security see section 10.2.3*/ + if (perm & GATT_PERM_WRITE_SIGNED) + { + perm = GATT_PERM_WRITE_ENCRYPTED; + } + else + { + perm = GATT_PERM_WRITE_ENC_MITM; + } + } + + if ((op_code == GATT_SIGN_CMD_WRITE) && !(perm & GATT_WRITE_SIGNED_PERM)) + { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_DEBUG0( "gatts_write_attr_perm_check - sign cmd write not allowed"); + } + if ((op_code == GATT_SIGN_CMD_WRITE) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED)) + { + status = GATT_INVALID_PDU; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - Error!! sign cmd write sent on a encypted link"); + } + else if (!(perm & GATT_WRITE_ALLOWED)) + { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_WRITE_NOT_PERMIT"); + } + else if ((perm & GATT_WRITE_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED)) + { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION"); + } + else if ((perm & GATT_WRITE_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) + { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: MITM required"); + } + else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) + { + status = GATT_INSUF_ENCRYPTION; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_ENCRYPTION"); + } + else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) + { + status = GATT_INSUF_KEY_SIZE; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_KEY_SIZE"); + } + else /* writable: must be char value declaration or char descritpors */ + { + if(p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) + { + switch (p_attr->uuid) + { + case GATT_UUID_CHAR_PRESENT_FORMAT:/* should be readable only */ + case GATT_UUID_CHAR_EXT_PROP:/* should be readable only */ + case GATT_UUID_CHAR_AGG_FORMAT: /* should be readable only */ + case GATT_UUID_CHAR_VALID_RANGE: + status = GATT_WRITE_NOT_PERMIT; + break; + + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + max_size = 2; + case GATT_UUID_CHAR_DESCRIPTION: + default: /* any other must be character value declaration */ + status = GATT_SUCCESS; + break; + } + } + else if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128) + { + status = GATT_SUCCESS; + } + else + { + status = GATT_INVALID_PDU; + } + + if (p_data == NULL && len > 0) + { + status = GATT_INVALID_PDU; + } + /* these attribute does not allow write blob */ +// btla-specific ++ + else if ( (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) && + (p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG) ) +// btla-specific -- + { + if (op_code == GATT_REQ_PREPARE_WRITE && offset != 0) /* does not allow write blob */ + { + status = GATT_NOT_LONG; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_NOT_LONG"); + } + else if (len != max_size) /* data does not match the required format */ + { + status = GATT_INVALID_PDU; + GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INVALID_PDU"); + } + else + { + status = GATT_SUCCESS; + } + } + } + break; + } + else + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + + return status; +} + +/******************************************************************************* +** +** Function allocate_attr_in_db +** +** Description Allocate a memory space for a new attribute, and link this +** attribute into the database attribute list. +** +** +** Parameter p_db : database pointer. +** service : type of attribute to be added. +** +** Returns pointer to the newly allocated attribute. +** +*******************************************************************************/ +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, UINT16 uuid16, UINT8 *uuid128, tGATT_PERM perm) +{ + tGATT_ATTR16 *p_attr16 = NULL, *p_last; + tGATT_ATTR128 *p_attr128 = NULL; + UINT16 len = (uuid16 == 0) ? sizeof(tGATT_ATTR128): sizeof(tGATT_ATTR16); + + GATT_TRACE_DEBUG1("allocate attr %d bytes ",len); + + if (uuid16 == GATT_ILLEGAL_UUID && uuid128 == NULL) + { + GATT_TRACE_ERROR0("illegal UUID"); + return NULL; + } + + if (p_db->end_handle <= p_db->next_handle) + { + GATT_TRACE_DEBUG2("handle space full. handle_max = %d next_handle = %d", + p_db->end_handle, p_db->next_handle); + return NULL; + } + + if (p_db->mem_free < len) + { + if (!allocate_svc_db_buf(p_db)) + { + GATT_TRACE_ERROR0("allocate_attr_in_db failed, no resources"); + return NULL; + } + } + + p_attr16 = (tGATT_ATTR16 *) p_db->p_free_mem; + p_attr128 = (tGATT_ATTR128 *) p_db->p_free_mem; + + memset(p_attr16, 0, len); + + if (uuid16 != GATT_ILLEGAL_UUID) + { + p_attr16->uuid_type = GATT_ATTR_UUID_TYPE_16; + p_attr16->uuid = uuid16; + } + else + { + p_attr128->uuid_type = GATT_ATTR_UUID_TYPE_128; + memcpy(p_attr128->uuid, uuid128, LEN_UUID_128); + } + + p_db->p_free_mem += len; + p_db->mem_free -= len; + + p_attr16->handle = p_db->next_handle++; + p_attr16->permission = perm; + p_attr16->p_next = NULL; + + /* link the attribute record into the end of DB */ + if (p_db->p_attr_list == NULL) + p_db->p_attr_list = p_attr16; + else + { + p_last = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_last != NULL && p_last->p_next != NULL) + p_last = (tGATT_ATTR16 *)p_last->p_next; + + p_last->p_next = p_attr16; + } + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) + { + GATT_TRACE_DEBUG3("=====> handle = [0x%04x] uuid = [0x%04x] perm=0x%02x ", + p_attr16->handle, p_attr16->uuid, p_attr16->permission); + } + else + { + GATT_TRACE_DEBUG4("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x ", + p_attr128->handle, p_attr128->uuid[0],p_attr128->uuid[1], + p_attr128->permission); + } + return(void *)p_attr16; +} + +/******************************************************************************* +** +** Function deallocate_attr_in_db +** +** Description Free an attribute within the database. +** +** Parameter p_db: database pointer. +** p_attr: pointer to the attribute record to be freed. +** +** Returns BOOLEAN: success +** +*******************************************************************************/ +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr) +{ + tGATT_ATTR16 *p_cur, *p_next; + BOOLEAN found = FALSE; + + if (p_db->p_attr_list == NULL) + return found; + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) + { + if (p_next == p_attr) + { + p_cur->p_next = p_next->p_next; + found = TRUE; + } + } + if (p_cur == p_attr && p_cur == p_db->p_attr_list) + { + p_db->p_attr_list = p_cur->p_next; + found = TRUE; + } + /* else attr not found */ + if ( found) + p_db->next_handle --; + + return found; +} + +/******************************************************************************* +** +** Function copy_extra_byte_in_db +** +** Description Utility function to allocate extra bytes memory in DB and copy +** the value from a source place. +** +** +** Parameter p_db: database pointer. +** p_dst: destination data pointer. +** p_src: source data pointer. +** len: data length to be copied. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len) +{ + UINT8 *p = (UINT8 *)*p_dst; + + if (p_db->mem_free < len) + { + if (!allocate_svc_db_buf(p_db)) + { + GATT_TRACE_ERROR0("copy_extra_byte_in_db failed, no resources"); + return FALSE; + } + } + + p = p_db->p_free_mem; + p_db->p_free_mem += len; + p_db->mem_free -= len; + memset((void *)p, 0, len); + *p_dst = (void *)p; + + return TRUE; +} + +/******************************************************************************* +** +** Function allocate_svc_db_buf +** +** Description Utility function to allocate extra buffer for service database. +** +** Returns TRUE if allocation succeed, otherwise FALSE. +** +*******************************************************************************/ +static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) +{ + BT_HDR *p_buf; + + GATT_TRACE_DEBUG0("allocate_svc_db_buf allocating extra buffer"); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf(GATT_DB_POOL_ID)) == NULL) + { + GATT_TRACE_ERROR0("allocate_svc_db_buf failed, no resources"); + return FALSE; + } + + memset(p_buf, 0, GKI_get_buf_size(p_buf)); + p_db->p_free_mem = (UINT8 *) p_buf; + p_db->mem_free = GKI_get_buf_size(p_buf); + + GKI_enqueue(&p_db->svc_buffer, p_buf); + + return TRUE; + +} + +/******************************************************************************* +** +** Function gatts_send_app_read_request +** +** Description Send application read request callback +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id) +{ + tGATTS_DATA sr_data; + UINT8 i_rcb; + tGATT_SR_REG *p_sreg; + UINT16 conn_id; + + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + + if (trans_id == 0) + { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + gatt_sr_update_cback_cnt(p_tcb, p_sreg->gatt_if, TRUE, TRUE); + } + + if (trans_id != 0 ) + { + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + sr_data.read_req.handle = handle; + sr_data.read_req.is_long = (BOOLEAN)(op_code == GATT_REQ_READ_BLOB); + sr_data.read_req.offset = offset; + + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_READ, &sr_data); + return(tGATT_STATUS) GATT_PENDING; + } + else + return(tGATT_STATUS) GATT_BUSY; /* max pending command, application error */ + +} + +/******************************************************************************* +** +** Function gatts_db_add_service_declaration +** +** Description Update a service database service declaration record. +** +** Parameter p_db: database pointer. +** service: UUID of the service. +** +** Returns void +** +*******************************************************************************/ +static void gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID service, BOOLEAN is_pri) +{ + tGATT_ATTR16 *p_attr; + UINT16 service_type = is_pri ? GATT_UUID_PRI_SERVICE: GATT_UUID_SEC_SERVICE; + + GATT_TRACE_DEBUG0( "add_service_declaration"); + + /* add service declration record */ + if ((p_attr = (tGATT_ATTR16 *)(allocate_attr_in_db(p_db, service_type, NULL, GATT_PERM_READ))) != NULL) + { + if (copy_extra_byte_in_db (p_db, (void **)&p_attr->p_value, sizeof(tBT_UUID))) + { + memcpy (&p_attr->p_value->uuid, &service, sizeof(tBT_UUID)); + } + } +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h new file mode 100644 index 0000000..44a2bc1 --- /dev/null +++ b/stack/gatt/gatt_int.h @@ -0,0 +1,671 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef GATT_INT_H +#define GATT_INT_H + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + + +#include "bt_trace.h" +#include "gatt_api.h" +#include "btm_ble_api.h" +#include "btu.h" + +#include + + +#define GATT_CREATE_CONN_ID(tcb_idx, gatt_if) ((UINT16) ((((UINT8)(tcb_idx) ) << 8) | ((UINT8) (gatt_if)))) +#define GATT_GET_TCB_IDX(conn_id) ((UINT8) (((UINT16) (conn_id)) >> 8)) +#define GATT_GET_GATT_IF(conn_id) ((tGATT_IF)((UINT8) (conn_id))) + +#define GATT_GET_SR_REG_PTR(index) (&gatt_cb.sr_reg[(UINT8) (index)]); +#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ + +/* security action for GATT write and read request */ +#define GATT_SEC_NONE 0 +#define GATT_SEC_OK 1 +#define GATT_SEC_ENCRYPT 2 /* encrypt the link with current key */ +#define GATT_SEC_ENCRYPT_NO_MITM 3 /* unauthenticated encryption or better */ +#define GATT_SEC_ENCRYPT_MITM 4 /* authenticated encryption */ +#define GATT_SEC_SIGN_DATA 5 /* compute the signature for the write cmd */ +typedef UINT8 tGATT_SEC_ACTION; + + +#define GATT_ATTR_OP_SPT_MTU (0x00000001 << 0) +#define GATT_ATTR_OP_SPT_FIND_INFO (0x00000001 << 1) +#define GATT_ATTR_OP_SPT_FIND_BY_TYPE (0x00000001 << 2) +#define GATT_ATTR_OP_SPT_READ_BY_TYPE (0x00000001 << 3) +#define GATT_ATTR_OP_SPT_READ (0x00000001 << 4) +#define GATT_ATTR_OP_SPT_MULT_READ (0x00000001 << 5) +#define GATT_ATTR_OP_SPT_READ_BLOB (0x00000001 << 6) +#define GATT_ATTR_OP_SPT_READ_BY_GRP_TYPE (0x00000001 << 7) +#define GATT_ATTR_OP_SPT_WRITE (0x00000001 << 8) +#define GATT_ATTR_OP_SPT_WRITE_CMD (0x00000001 << 9) +#define GATT_ATTR_OP_SPT_PREP_WRITE (0x00000001 << 10) +#define GATT_ATTR_OP_SPT_EXE_WRITE (0x00000001 << 11) +#define GATT_ATTR_OP_SPT_HDL_VALUE_CONF (0x00000001 << 12) +#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) + +#define GATT_INDEX_INVALID 0xff + +#define GATT_PENDING_REQ_NONE 0 + + +#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ +#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ +#define GATT_AUTH_SIGN_LEN 12 + +#define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */ + +/* wait for ATT cmd response timeout value */ +#define GATT_WAIT_FOR_RSP_TOUT 30 + +/* characteristic descriptor type */ +#define GATT_DESCR_EXT_DSCPTOR 1 /* Characteristic Extended Properties */ +#define GATT_DESCR_USER_DSCPTOR 2 /* Characteristic User Description */ +#define GATT_DESCR_CLT_CONFIG 3 /* Client Characteristic Configuration */ +#define GATT_DESCR_SVR_CONFIG 4 /* Server Characteristic Configuration */ +#define GATT_DESCR_PRES_FORMAT 5 /* Characteristic Presentation Format */ +#define GATT_DESCR_AGGR_FORMAT 6 /* Characteristic Aggregate Format */ +#define GATT_DESCR_VALID_RANGE 7 /* Characteristic Valid Range */ +#define GATT_DESCR_UNKNOWN 0xff + +#define GATT_SEC_FLAG_LKEY_UNAUTHED BTM_SEC_FLAG_LKEY_KNOWN +#define GATT_SEC_FLAG_LKEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED +#define GATT_SEC_FLAG_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED +typedef UINT8 tGATT_SEC_FLAG; + +/* Find Information Response Type +*/ +#define GATT_INFO_TYPE_PAIR_16 0x01 +#define GATT_INFO_TYPE_PAIR_128 0x02 + +/* GATT client FIND_TYPE_VALUE_Request data */ +typedef struct +{ + tBT_UUID uuid; /* type of attribute to be found */ + UINT16 s_handle; /* starting handle */ + UINT16 e_handle; /* ending handle */ + UINT16 value_len; /* length of the attribute value */ + UINT8 value[GATT_MAX_MTU_SIZE]; /* pointer to the attribute value to be found */ +} tGATT_FIND_TYPE_VALUE; + +/* client request message to ATT protocol +*/ +typedef union +{ + tGATT_READ_BY_TYPE browse; /* read by type request */ + tGATT_FIND_TYPE_VALUE find_type_value;/* find by type value */ + tGATT_READ_MULTI read_multi; /* read multiple request */ + tGATT_READ_PARTIAL read_blob; /* read blob */ + tGATT_VALUE attr_value; /* write request */ + /* prepare write */ + /* write blob */ + UINT16 handle; /* read, handle value confirmation */ + UINT16 mtu; + tGATT_EXEC_FLAG exec_write; /* execute write */ +}tGATT_CL_MSG; + +/* error response strucutre */ +typedef struct +{ + UINT16 handle; + UINT8 cmd_code; + UINT8 reason; +}tGATT_ERROR; + +/* server response message to ATT protocol +*/ +typedef union +{ + /* data type member event */ + tGATT_VALUE attr_value; /* READ, HANDLE_VALUE_IND, PREPARE_WRITE */ + /* READ_BLOB, READ_BY_TYPE */ + tGATT_ERROR error; /* ERROR_RSP */ + UINT16 handle; /* WRITE, WRITE_BLOB */ + UINT16 mtu; /* exchange MTU request */ +} tGATT_SR_MSG; + +/* Characteristic declaration attribute value +*/ +typedef struct +{ + tGATT_CHAR_PROP property; + UINT16 char_val_handle; +} tGATT_CHAR_DECL; + +/* attribute value maintained in the server database +*/ +typedef union +{ + tBT_UUID uuid; /* service declaration */ + tGATT_CHAR_DECL char_decl; /* characteristic declaration */ + tGATT_INCL_SRVC incl_handle; /* included service */ + +} tGATT_ATTR_VALUE; + +/* Attribute UUID type +*/ +#define GATT_ATTR_UUID_TYPE_16 0 +#define GATT_ATTR_UUID_TYPE_128 1 +typedef UINT8 tGATT_ATTR_UUID_TYPE; + +/* 16 bits UUID Attribute in server database +*/ +typedef struct +{ + void *p_next; /* pointer to the next attribute, + either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + UINT16 handle; + UINT16 uuid; +} tGATT_ATTR16; + +/* 128 bits UUID Attribute in server database +*/ +typedef struct +{ + void *p_next; /* pointer to the next attribute, + either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + UINT16 handle; + UINT8 uuid[LEN_UUID_128]; +} tGATT_ATTR128; + +/* Service Database definition +*/ +typedef struct +{ + void *p_attr_list; /* pointer to the first attribute, + either tGATT_ATTR16 or tGATT_ATTR128 */ + UINT8 *p_free_mem; /* Pointer to free memory */ + BUFFER_Q svc_buffer; /* buffer queue used for service database */ + UINT32 mem_free; /* Memory still available */ + UINT16 end_handle; /* Last handle number */ + UINT16 next_handle; /* Next usable handle value */ +} tGATT_SVC_DB; + +/* Data Structure used for GATT server */ +/* A GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ +typedef struct +{ + tGATT_SVC_DB *p_db; /* pointer to the service database */ + //tGATT_SR_CBACK sr_cb; /* server callback functions */ + tBT_UUID app_uuid; /* applicatino UUID */ + UINT32 sdp_handle; /* primamry service SDP handle */ + UINT16 service_instance; /* service instance number */ + UINT16 type; /* service type UUID, primary or secondary */ + UINT16 s_hdl; /* service starting handle */ + UINT16 e_hdl; /* service ending handle */ + tGATT_IF gatt_if; /* this service is belong to which application */ + BOOLEAN in_use; +} tGATT_SR_REG; + + +/* Data Structure used for GATT server */ +/* An GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ + +typedef struct +{ + tBT_UUID app_uuid128; + tGATT_CBACK app_cb; + tGATT_IF gatt_if; /* one based */ + BOOLEAN in_use; +} tGATT_REG; + + + + +/* command queue for each connection */ +typedef struct +{ + BT_HDR *p_cmd; + UINT16 clcb_idx; + UINT8 op_code; + BOOLEAN to_send; +}tGATT_CMD_Q; + + +#if GATT_MAX_SR_PROFILES <= 8 +typedef UINT8 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 16 +typedef UINT16 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 32 +typedef UINT32 tGATT_APP_MASK; +#endif + +/* command details for each connection */ +typedef struct +{ + BT_HDR *p_rsp_msg; + UINT32 trans_id; + tGATT_READ_MULTI multi_req; + BUFFER_Q multi_rsp_q; + UINT16 handle; + UINT8 op_code; + UINT8 status; + UINT8 cback_cnt[GATT_MAX_APPS]; +} tGATT_SR_CMD; + +#define GATT_CH_CLOSE 0 +#define GATT_CH_CLOSING 1 +#define GATT_CH_CONN 2 +#define GATT_CH_CFG 3 +#define GATT_CH_OPEN 4 +#define GATT_CH_W4_SEC_COMP 5 +#define GATT_CH_W4_DATA_SIGN_COMP 6 + +typedef UINT8 tGATT_CH_STATE; + +#define GATT_GATT_START_HANDLE 1 +#define GATT_GAP_START_HANDLE 20 +#define GATT_APP_START_HANDLE 40 + +typedef struct hdl_cfg +{ + UINT16 gatt_start_hdl; + UINT16 gap_start_hdl; + UINT16 app_start_hdl; +}tGATT_HDL_CFG; + +typedef struct hdl_list_elem +{ + struct hdl_list_elem *p_next; + struct hdl_list_elem *p_prev; + tGATTS_HNDL_RANGE asgn_range; /* assigned handle range */ + tGATT_SVC_DB svc_db; + BOOLEAN in_use; +}tGATT_HDL_LIST_ELEM; + +typedef struct +{ + tGATT_HDL_LIST_ELEM *p_first; + tGATT_HDL_LIST_ELEM *p_last; + UINT16 count; +}tGATT_HDL_LIST_INFO; + + +typedef struct srv_list_elem +{ + struct srv_list_elem *p_next; + struct srv_list_elem *p_prev; + UINT16 s_hdl; + UINT8 i_sreg; + BOOLEAN in_use; + BOOLEAN is_primary; +}tGATT_SRV_LIST_ELEM; + + +typedef struct +{ + tGATT_SRV_LIST_ELEM *p_last_primary; + tGATT_SRV_LIST_ELEM *p_first; + tGATT_SRV_LIST_ELEM *p_last; + UINT16 count; +}tGATT_SRV_LIST_INFO; + +typedef struct +{ + void *p_clcb; /* which clcb is doing encryption */ + tGATT_SEC_ACTION sec_act; + BD_ADDR peer_bda; + UINT32 trans_id; + + UINT16 att_lcid; /* L2CAP channel ID for ATT */ + UINT16 payload_size; + + tGATT_CH_STATE ch_state; + UINT8 ch_flags; + + tGATT_IF app_hold_link[GATT_MAX_APPS]; + + /* server needs */ + /* server response data */ + tGATT_SR_CMD sr_cmd; + UINT16 indicate_handle; + BUFFER_Q pending_ind_q; + + TIMER_LIST_ENT conf_timer_ent; /* peer confirm to indication timer */ + + UINT8 prep_cnt[GATT_MAX_APPS]; + UINT8 ind_count; + + tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; + TIMER_LIST_ENT rsp_timer_ent; /* peer response timer */ + TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ + UINT8 pending_cl_req; + UINT8 next_slot_inq; /* index of next available slot in queue */ + + BOOLEAN in_use; + UINT8 tcb_idx; +} tGATT_TCB; + +/* logic channel */ +typedef struct +{ + UINT16 next_disc_start_hdl; /* starting handle for the next inc srvv discovery */ + tGATT_DISC_RES result; + BOOLEAN wait_for_read_rsp; +} tGATT_READ_INC_UUID128; +typedef struct +{ + tGATT_TCB *p_tcb; /* associated TCB of this CLCB */ + tGATT_REG *p_reg; /* owner of this CLCB */ + UINT8 sccb_idx; + UINT8 *p_attr_buf; /* attribute buffer for read multiple, prepare write */ + tBT_UUID uuid; + UINT16 conn_id; /* connection handle */ + UINT16 clcb_idx; + UINT16 s_handle; /* starting handle of the active request */ + UINT16 e_handle; /* ending handle of the active request */ + UINT16 counter; /* used as offset, attribute length, num of prepare write */ + UINT16 start_offset; + tGATT_AUTH_REQ auth_req; /* authentication requirement */ + UINT8 operation; /* one logic channel can have one operation active */ + UINT8 op_subtype; /* operation subtype */ + UINT8 status; /* operation status */ + BOOLEAN first_read_blob_after_read; + tGATT_READ_INC_UUID128 read_uuid128; + BOOLEAN in_use; +} tGATT_CLCB; + +#define GATT_SIGN_WRITE 1 +#define GATT_VERIFY_SIGN_DATA 2 + +typedef struct +{ + BT_HDR hdr; + tGATT_CLCB *p_clcb; +}tGATT_SIGN_WRITE_OP; + +typedef struct +{ + BT_HDR hdr; + tGATT_TCB *p_tcb; + BT_HDR *p_data; + +}tGATT_VERIFY_SIGN_OP; + + +typedef struct +{ + UINT16 clcb_idx; + BOOLEAN in_use; +} tGATT_SCCB; + +typedef struct +{ + UINT16 handle; + UINT16 uuid; + UINT32 service_change; +}tGATT_SVC_CHG; + +typedef struct +{ + tGATT_IF gatt_if[GATT_MAX_APPS]; + BD_ADDR remote_bda; + BOOLEAN in_use; +}tGATT_BG_CONN_DEV; + + +typedef struct +{ + UINT16 conn_id; + BOOLEAN in_use; + BOOLEAN connected; + BD_ADDR bda; +}tGATT_PROFILE_CLCB; + +typedef struct +{ + tGATT_TCB tcb[GATT_MAX_PHY_CHANNEL]; + BUFFER_Q sign_op_queue; + + tGATT_SR_REG sr_reg[GATT_MAX_SR_PROFILES]; + UINT16 next_handle; /* next available handle */ + tGATT_SVC_CHG gattp_attr; /* GATT profile attribute service change */ + tGATT_IF gatt_if; + tGATT_HDL_LIST_INFO hdl_list_info; + tGATT_HDL_LIST_ELEM hdl_list[GATT_MAX_SR_PROFILES]; + tGATT_SRV_LIST_INFO srv_list_info; + tGATT_SRV_LIST_ELEM srv_list[GATT_MAX_SR_PROFILES]; + + BUFFER_Q srv_chg_clt_q; /* service change clients queue */ + BUFFER_Q pending_new_srv_start_q; /* pending new service start queue */ + tGATT_REG cl_rcb[GATT_MAX_APPS]; + tGATT_CLCB clcb[GATT_CL_MAX_LCB]; /* connection link control block*/ + tGATT_SCCB sccb[GATT_MAX_SCCB]; /* sign complete callback function GATT_MAX_SCCB <= GATT_CL_MAX_LCB */ + UINT8 trace_level; + UINT16 def_mtu_size; + +#if GATT_CONFORMANCE_TESTING == TRUE + BOOLEAN enable_err_rsp; + UINT8 req_op_code; + UINT8 err_status; +#endif + + tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS]; + UINT16 handle_of_h_r; /* Handle of the handles reused characteristic value */ + + tGATT_APPL_INFO cb_info; + + + + tGATT_HDL_CFG hdl_cfg; + tGATT_BG_CONN_DEV bgconn_dev[GATT_MAX_BG_CONN_DEV]; + +} tGATT_CB; + + +#define GATT_SIZE_OF_SRV_CHG_HNDL_RANGE 4 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +GATT_API extern tGATT_CB gatt_cb; +#else +GATT_API extern tGATT_CB *gatt_cb_ptr; +#define gatt_cb (*gatt_cb_ptr) +#endif + +#if GATT_CONFORMANCE_TESTING == TRUE +GATT_API extern void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status); +#endif + +#ifdef __cplusplus +} +#endif + +/* internal functions */ +extern void gatt_init (void); + +/* from gatt_main.c */ +extern BOOLEAN gatt_disconnect (BD_ADDR rem_bda); +extern BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr); +extern BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb); +extern void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern void gatt_update_app_use_link_flag ( tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link); + +extern void gatt_profile_db_init(void); +extern void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state); +extern tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb); +extern void gatt_init_srv_chg(void); +extern void gatt_proc_srv_chg (void); +extern void gatt_send_srv_chg_ind (BD_ADDR peer_bda); +extern void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt); +extern void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda); + +/* from gatt_attr.c */ +extern UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda); +extern tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda); +extern BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id); +extern tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda); + + +/* Functions provided by att_protocol.c */ +extern tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg); +extern BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg); +extern tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg); +extern BOOLEAN attp_send_msg_to_L2CAP(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP); + +/* utility functions */ +extern UINT8 * gatt_dbg_op_name(UINT8 op_code); +extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl); +extern BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid, UINT16 len, UINT8 **p_data); +extern UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid); +extern BOOLEAN gatt_uuid_compare(tBT_UUID src, tBT_UUID tar); +extern void gatt_sr_get_sec_info(BD_ADDR rem_bda, BOOLEAN le_conn, UINT8 *p_sec_flag, UINT8 *p_key_size); +extern void gatt_start_rsp_timer(tGATT_TCB *p_tcb); +extern void gatt_start_conf_timer(tGATT_TCB *p_tcb); +extern void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb); +extern tGATT_STATUS gatt_send_error_rsp(tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, UINT16 handle, BOOLEAN deq); +extern void gatt_dbg_display_uuid(tBT_UUID bt_uuid); + +extern tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); + +extern BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb); +extern tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda); + +extern BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx); +extern void gatt_set_srv_chg(void); +extern void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr); +extern tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind); +extern tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start( tGATTS_HNDL_RANGE *p_new_srv_start); +extern void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id); + +/* reserved handle list */ +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle); +extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); +extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); +extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); +extern void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list); +extern BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove); +extern BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove); +extern tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg); + +/* for background connection */ +extern BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr); +extern BOOLEAN gatt_add_bg_dev_list(tGATT_IF gatt_if, BD_ADDR bd_addr); +extern BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if); +extern BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr); +extern UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr); +extern BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if); +extern BOOLEAN gatt_remove_bg_dev_from_list(tGATT_IF gatt_if, BD_ADDR bd_addr); +extern tGATT_BG_CONN_DEV * gatt_find_bg_dev(BD_ADDR remote_bda); +extern void gatt_deregister_bgdev_list(tGATT_IF gatt_if); +extern void gatt_reset_bgdev_list(void); + +/* server function */ +extern UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle); +extern UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list); +extern tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, UINT32 trans_id, UINT8 op_code, tGATT_STATUS status, tGATTS_RSP *p_msg); +extern void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_sr_send_req_callback(UINT16 conn_id, UINT32 trans_id, + UINT8 op_code, tGATTS_DATA *p_req_data); +extern UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle); +extern BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda); + +/* */ + +extern tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if); +extern BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id); +extern tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id); +extern void gatt_clcb_dealloc (tGATT_CLCB *p_clcb); + +extern void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); +extern void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); + +extern BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if); +extern UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb); +extern UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda); +extern tGATT_TCB * gatt_find_tcb_by_cid(UINT16 lcid); +extern tGATT_TCB * gatt_allocate_tcb_by_bdaddr(BD_ADDR bda); +extern tGATT_TCB * gatt_get_tcb_by_idx(UINT8 tcb_idx); +extern tGATT_TCB * gatt_find_tcb_by_addr(BD_ADDR bda); + + +/* GATT client functions */ +extern void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb); +extern UINT8 gatt_send_write_msg(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle, + UINT16 len, UINT16 offset, UINT8 *p_data); +extern void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason); +extern void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data); + +extern void gatt_act_discovery(tGATT_CLCB *p_clcb); +extern void gatt_act_read(tGATT_CLCB *p_clcb, UINT16 offset); +extern void gatt_act_write(tGATT_CLCB *p_clcb); +extern UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, UINT16 e_handle, + tBT_UUID uuid); +extern tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_opcode); +extern BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf); +extern void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag); + +/* gatt_auth.c */ +extern BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb); +extern void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ); +extern tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb); +extern tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb); +extern void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act); + +/* gatt_db.c */ +extern BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID service, BOOLEAN is_pri, UINT16 s_hdl, UINT16 num_handle); +extern UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, tBT_UUID service); +extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tGATT_CHAR_PROP property, tBT_UUID *p_char_uuid); +extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID *p_dscp_uuid); +extern tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, BT_HDR *p_rsp, UINT16 s_handle, + UINT16 e_handle, tBT_UUID type, UINT16 *p_len, tGATT_SEC_FLAG sec_flag, UINT8 key_size,UINT32 trans_id, UINT16 *p_cur_handle); +extern tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb,tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, UINT16 mtu,tGATT_SEC_FLAG sec_flag,UINT8 key_size,UINT32 trans_id); +extern tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code,UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size); +extern tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, BOOLEAN is_long, UINT16 handle, tGATT_SEC_FLAG sec_flag,UINT8 key_size); +extern void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary); +extern tBT_UUID * gatts_get_service_uuid (tGATT_SVC_DB *p_db); + +#endif + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_main.c b/stack/gatt/gatt_main.c new file mode 100644 index 0000000..06d87bb --- /dev/null +++ b/stack/gatt/gatt_main.c @@ -0,0 +1,1115 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main ATT functions + * + ******************************************************************************/ + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include "gki.h" +#include "gatt_int.h" +#include "l2c_api.h" +#include "btm_int.h" +#include "btm_ble_int.h" + +/* Configuration flags. */ +#define GATT_L2C_CFG_IND_DONE (1<<0) +#define GATT_L2C_CFG_CFM_DONE (1<<1) + +/********************************************************************************/ +/* 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); +static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf); + +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); +static void gatt_l2cif_disconnect_ind_cback (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void gatt_l2cif_disconnect_cfm_cback (UINT16 l2cap_cid, UINT16 result); +static void gatt_l2cif_data_ind_cback (UINT16 l2cap_cid, BT_HDR *p_msg); +static void gatt_send_conn_cback (BOOLEAN is_bg_conn, tGATT_TCB *p_tcb); + +static const tL2CAP_APPL_INFO dyn_info = +{ + gatt_l2cif_connect_ind_cback, + gatt_l2cif_connect_cfm_cback, + NULL, + gatt_l2cif_config_ind_cback, + gatt_l2cif_config_cfm_cback, + gatt_l2cif_disconnect_ind_cback, + gatt_l2cif_disconnect_cfm_cback, + NULL, + gatt_l2cif_data_ind_cback, + NULL +} ; + +#if GATT_DYNAMIC_MEMORY == FALSE +tGATT_CB gatt_cb; +#endif + +/******************************************************************************* +** +** Function gatt_init +** +** Description This function is enable the GATT profile on the device. +** It clears out the control blocks, and registers with L2CAP. +** +** Returns void +** +*******************************************************************************/ +void gatt_init (void) +{ + tL2CAP_FIXED_CHNL_REG fixed_reg; + + GATT_TRACE_DEBUG0("gatt_init()"); + + memset (&gatt_cb, 0, sizeof(tGATT_CB)); + +#if defined(GATT_INITIAL_TRACE_LEVEL) + gatt_cb.trace_level = GATT_INITIAL_TRACE_LEVEL; +#else + gatt_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + gatt_cb.def_mtu_size = GATT_DEF_BLE_MTU_SIZE; + GKI_init_q (&gatt_cb.sign_op_queue); + /* First, register fixed L2CAP channel for ATT over BLE */ + fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE; + fixed_reg.fixed_chnl_opts.max_transmit = 0xFF; + fixed_reg.fixed_chnl_opts.rtrans_tout = 2000; + fixed_reg.fixed_chnl_opts.mon_tout = 12000; + fixed_reg.fixed_chnl_opts.mps = 670; + fixed_reg.fixed_chnl_opts.tx_win_sz = 1; + + fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback; + fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind; + fixed_reg.default_idle_tout = 0xffff; /* 0xffff default idle timeout */ + + L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg); + + /* Now, register with L2CAP for ATT PSM over BR/EDR */ + if (!L2CA_Register (BT_PSM_ATT, (tL2CAP_APPL_INFO *) &dyn_info)) + { + GATT_TRACE_ERROR0 ("ATT Dynamic Registration failed"); + } + + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + + gatt_cb.hdl_cfg.gatt_start_hdl = GATT_GATT_START_HANDLE; + gatt_cb.hdl_cfg.gap_start_hdl = GATT_GAP_START_HANDLE; + gatt_cb.hdl_cfg.app_start_hdl = GATT_APP_START_HANDLE; + gatt_profile_db_init(); + +} + + + +/******************************************************************************* +** +** Function gatt_connect +** +** Description This function is called to initiate a connection to a peer device. +** +** Parameter rem_bda: remote device address to connect to. +** +** Returns TRUE if connection is started, otherwise return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb) +{ + BOOLEAN gatt_ret = TRUE; + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; + + BTM_ReadDevInfo(rem_bda, &dev_type, &addr_type); + + gatt_set_ch_state(p_tcb, GATT_CH_CONN); + + if (dev_type == BT_DEVICE_TYPE_BLE) + { + p_tcb->att_lcid = L2CAP_ATT_CID; + gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda); + } + else + { + if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) == 0) + gatt_ret = FALSE; + } + + return gatt_ret; +} + +/******************************************************************************* +** +** Function gatt_disconnect +** +** Description This function is called to disconnect to an ATT device. +** +** Parameter rem_bda: remote device address to disconnect from. +** +** Returns TRUE: if connection found and to be disconnected; otherwise +** return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_disconnect (BD_ADDR rem_bda) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(rem_bda); + BOOLEAN ret = FALSE; + tGATT_CH_STATE ch_state; + GATT_TRACE_DEBUG0 ("gatt_disconnect "); + + if (p_tcb != NULL) + { + ret = TRUE; + if ( (ch_state = gatt_get_ch_state(p_tcb)) != GATT_CH_CLOSING ) + { + if (p_tcb->att_lcid == L2CAP_ATT_CID) + { + if (ch_state == GATT_CH_OPEN) + /* only LCB exist between remote device and local */ + ret = L2CA_RemoveFixedChnl (L2CAP_ATT_CID, rem_bda); + else + { + gatt_set_ch_state(p_tcb, GATT_CH_CLOSING); + ret = L2CA_CancelBleConnectReq (rem_bda); + } + } + else + { + ret = L2CA_DisconnectReq(p_tcb->att_lcid); + } + } + else + { + GATT_TRACE_DEBUG0 ("gatt_disconnect already in closing state"); + } + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_update_app_hold_link_status +** +** Description Update the application use link status +** +** Returns void. +** +*******************************************************************************/ +void gatt_update_app_hold_link_status (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add) +{ + UINT8 i; + BOOLEAN found=FALSE; + + if (p_tcb == NULL) + { + GATT_TRACE_ERROR0("gatt_update_app_hold_link_status p_tcb=NULL"); + return; + } + + + for (i=0; iapp_hold_link[i] == gatt_if) + { + found = TRUE; + if (!is_add) + { + p_tcb->app_hold_link[i] = 0; + break; + } + } + } + + if (!found && is_add) + { + for (i=0; iapp_hold_link[i] == 0) + { + p_tcb->app_hold_link[i] = gatt_if; + found = TRUE; + break; + } + } + } + + GATT_TRACE_DEBUG4("gatt_update_app_hold_link_status found=%d[1-found] idx=%d gatt_if=%d is_add=%d", found, i, gatt_if, is_add); + +} + +/******************************************************************************* +** +** Function gatt_update_app_use_link_flag +** +** Description Update the application use link flag and optional to check the acl link +** if the link is up then set the idle time out accordingly +** +** Returns void. +** +*******************************************************************************/ +void gatt_update_app_use_link_flag (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link) +{ + GATT_TRACE_DEBUG2("gatt_update_app_use_link_flag is_add=%d chk_link=%d", + is_add, check_acl_link); + + gatt_update_app_hold_link_status(gatt_if, p_tcb, is_add); + + if (check_acl_link && + p_tcb && + (BTM_GetHCIConnHandle(p_tcb->peer_bda) != GATT_INVALID_ACL_HANDLE)) + { + if (is_add) + { + GATT_TRACE_DEBUG0("GATT disables link idle timer"); + /* acl link is connected disable the idle timeout */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT); + } + else + { + if (!gatt_num_apps_hold_link(p_tcb)) + { + /* acl link is connected but no application needs to use the link + so set the timeout value to GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP seconds */ + GATT_TRACE_DEBUG1("GATT starts link idle timer =%d sec", GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP); + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP); + } + + } + } +} + +/******************************************************************************* +** +** Function gatt_act_connect +** +** Description GATT connection initiation. +** +** Returns void. +** +*******************************************************************************/ +BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr) +{ + BOOLEAN ret = FALSE; + tGATT_TCB *p_tcb; + + GATT_TRACE_DEBUG0("gatt_act_connect"); + + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL) + { + ret = TRUE; + if(gatt_get_ch_state(p_tcb) == GATT_CH_CLOSING ) + { + /* need to complete the closing first */ + ret = FALSE; + } + } + else + { + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL) + { + if (!gatt_connect(bd_addr, p_tcb)) + { + GATT_TRACE_ERROR0("gatt_connect failed"); + memset(p_tcb, 0, sizeof(tGATT_TCB)); + } + else + ret = TRUE; + } + else + { + ret = 0; + GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if); + } + } + + if (ret) + { + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE); + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_le_connect_cback +** +** Description This callback function is called by L2CAP to indicate that +** the ATT fixed channel for LE is +** connected (conn = TRUE)/disconnected (conn = FALSE). +** +*******************************************************************************/ +static void gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason) +{ + + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr); + + BOOLEAN check_srv_chg = FALSE; + tGATTS_SRV_CHG *p_srv_chg_clt=NULL; + BOOLEAN is_bg_conn = FALSE; + + + GATT_TRACE_DEBUG3 ("GATT ATT protocol channel with BDA: %08x%04x is %s", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], (connected) ? "connected" : "disconnected"); + + + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) + { + check_srv_chg = TRUE; + } + else + { + if (btm_sec_is_a_bonded_dev(bd_addr)) + gatt_add_a_bonded_dev_for_srv_chg(bd_addr); + } + + if (connected) + { + GATT_TRACE_DEBUG1("connected is TRUE reason=%d",reason ); + /* BR/EDR lik, ignore this callback */ + if (reason == 0) + return; + + /* do we have a channel initiating a connection? */ + if (p_tcb) + { + if (check_srv_chg) + gatt_chk_srv_chg (p_srv_chg_clt); + /* we are initiating connection */ + if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN) + { + /* send callback */ + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + + gatt_send_conn_cback(FALSE, p_tcb); + } + else /* there was an exisiting link, ignore the callback */ + { + GATT_TRACE_ERROR0("connection already up, ignore it"); + return; + } + } + /* this is incoming connection or background connection callback */ + else + { + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL) + { + p_tcb->att_lcid = L2CAP_ATT_CID; + + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + if (L2CA_GetBleConnRole(p_tcb->peer_bda)== HCI_ROLE_MASTER) + { + is_bg_conn = TRUE; + } + gatt_send_conn_cback (is_bg_conn, p_tcb); + if (check_srv_chg) + { + gatt_chk_srv_chg (p_srv_chg_clt); + } + } + else + { + GATT_TRACE_ERROR0("CCB max out, no rsources"); + } + } + } + else + { + gatt_cleanup_upon_disc(bd_addr, reason); + GATT_TRACE_DEBUG0 ("ATT disconnected"); + } +} + +/******************************************************************************* +** +** Function gatt_le_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the ATT +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the ATT +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tGATT_TCB *p_tcb; + + /* Find CCB based on bd addr */ + if ((p_tcb = gatt_find_tcb_by_addr (bd_addr)) != NULL && + gatt_get_ch_state(p_tcb) >= GATT_CH_OPEN) + { + gatt_data_process(p_tcb, p_buf); + } + else + { + GKI_freebuf (p_buf); + + if (p_tcb != NULL) + { + GATT_TRACE_WARNING1 ("ATT - Ignored L2CAP data while in state: %d", + gatt_get_ch_state(p_tcb)); + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_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 gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + /* do we already have a control channel for this peer? */ + UINT8 result = L2CAP_CONN_OK; + tL2CAP_CFG_INFO cfg; + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr); + + GATT_TRACE_ERROR1("Connection indication cid = %d", lcid); + /* new connection ? */ + if (p_tcb == NULL) + { + /* allocate tcb */ + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) == NULL) + { + /* no tcb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + else + p_tcb->att_lcid = lcid; + + } + else /* existing connection , reject it */ + { + result = L2CAP_CONN_NO_RESOURCES; + } + + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* transition to configuration state */ + gatt_set_ch_state(p_tcb, GATT_CH_CFG); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = GATT_MAX_MTU_SIZE; + + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function gatt_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tGATT_TCB *p_tcb; + tL2CAP_CFG_INFO cfg; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) + { + GATT_TRACE_DEBUG3("gatt_l2c_connect_cfm_cback result: %d ch_state: %d, lcid:0x%x", result, gatt_get_ch_state(p_tcb), p_tcb->att_lcid); + + /* if in correct state */ + if (gatt_get_ch_state(p_tcb) == GATT_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + gatt_set_ch_state(p_tcb, GATT_CH_CFG); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = GATT_MAX_MTU_SIZE; + L2CA_ConfigReq(lcid, &cfg); + } + /* else initiating connection failure */ + else + { + gatt_cleanup_upon_disc(p_tcb->peer_bda, result); + } + } + else /* wrong state, disconnect it */ + { + if (result == L2CAP_CONN_OK) + { + /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tGATT_TCB *p_tcb; + tGATTS_SRV_CHG *p_srv_chg_clt=NULL; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) + { + /* if in correct state */ + if ( gatt_get_ch_state(p_tcb) == GATT_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_tcb->ch_flags |= GATT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) + { + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) + { + gatt_chk_srv_chg(p_srv_chg_clt); + } + else + { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + + /* send callback */ + gatt_send_conn_cback(FALSE, p_tcb); + } + } + /* else failure */ + else + { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tGATT_TCB *p_tcb; + tGATTS_SRV_CHG *p_srv_chg_clt=NULL; + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) + { + /* GATT uses the smaller of our MTU and peer's MTU */ + if ( (p_cfg->mtu_present) && (p_cfg->mtu < L2CAP_DEFAULT_MTU) ) + p_tcb->payload_size = p_cfg->mtu; + else + p_tcb->payload_size = L2CAP_DEFAULT_MTU; + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_tcb->ch_flags |= GATT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tcb->ch_flags & GATT_L2C_CFG_CFM_DONE) + { + gatt_set_ch_state(p_tcb, GATT_CH_OPEN); + if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) + { + gatt_chk_srv_chg(p_srv_chg_clt); + } + else + { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + + /* send callback */ + gatt_send_conn_cback(FALSE, p_tcb); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda)) == 0) + reason = GATT_CONN_TERMINATE_PEER_USER; + + /* send disconnect callback */ + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason); + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) + { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + /* send disconnect callback */ + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda)) == 0) + reason = GATT_CONN_TERMINATE_LOCAL_HOST; + + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason); + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tGATT_TCB *p_tcb; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL && + gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) + { + /* process the data */ + gatt_data_process(p_tcb, p_buf); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + +/******************************************************************************* +** +** Function gatt_send_conn_cback +** +** Description Callback used to notify layer above about a connection. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_send_conn_cback(BOOLEAN is_bg_conn, tGATT_TCB *p_tcb) +{ + UINT8 i; + tGATT_REG *p_reg; + tGATT_BG_CONN_DEV *p_bg_dev=NULL; + UINT16 conn_id; + + if (is_bg_conn) + p_bg_dev = gatt_find_bg_dev(p_tcb->peer_bda); + + /* notifying all applications for the connection up event */ + for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) + { + if (p_reg->in_use) + { + if (p_bg_dev && gatt_is_bg_dev_for_app(p_bg_dev, p_reg->gatt_if)) + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, TRUE); + + if (p_reg->app_cb.p_conn_cb) + { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, TRUE, 0); + } + } + } + + + if (gatt_num_apps_hold_link(p_tcb)) + { + /* disable idle timeout if one or more clients are holding the link disable the idle timer */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT); + } +} + +/******************************************************************************* +** +** Function gatt_le_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the ATT +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the ATT +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 op_code, pseudo_op_code; + UINT16 msg_len; + + + if (p_buf->len > 0) + { + msg_len = p_buf->len - 1; + STREAM_TO_UINT8(op_code, p); + + /* remove the two MSBs associated with sign write and write cmd */ + pseudo_op_code = op_code & (~GATT_WRITE_CMD_MASK); + + if (pseudo_op_code < GATT_OP_CODE_MAX) + { + if (op_code == GATT_SIGN_CMD_WRITE) + { + gatt_verify_signature(p_tcb, p_buf); + return; + } + else + { + /* message from client */ + if ((op_code % 2) == 0) + gatt_server_handle_client_req (p_tcb, op_code, msg_len, p); + else + gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p); + } + } + else + { + GATT_TRACE_ERROR1 ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code); + } + } + else + { + GATT_TRACE_ERROR0 ("invalid data length, ignore"); + } + + GKI_freebuf (p_buf); +} + +/******************************************************************************* +** +** Function gatt_add_a_bonded_dev_for_srv_chg +** +** Description Add a bonded dev to the service changed client list +** +** Returns void +** +*******************************************************************************/ +void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda) +{ + tGATTS_SRV_CHG *p_buf; + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG srv_chg_clt; + + memcpy(srv_chg_clt.bda, bda, BD_ADDR_LEN); + srv_chg_clt.srv_changed = FALSE; + if ((p_buf = gatt_add_srv_chg_clt(&srv_chg_clt)) != NULL) + { + memcpy(req.srv_chg.bda, bda, BD_ADDR_LEN); + req.srv_chg.srv_changed = FALSE; + if (gatt_cb.cb_info.p_srv_chg_callback) + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_ADD_CLIENT, &req, NULL); + } + +} + +/******************************************************************************* +** +** Function gatt_send_srv_chg_ind +** +** Description This function is called to send a service chnaged indication to +** the specified bd address +** +** Returns void +** +*******************************************************************************/ +void gatt_send_srv_chg_ind (BD_ADDR peer_bda) +{ + UINT8 handle_range[GATT_SIZE_OF_SRV_CHG_HNDL_RANGE]; + UINT8 *p = handle_range; + UINT16 conn_id; + + GATT_TRACE_DEBUG0("gatt_send_srv_chg_ind"); + + if (gatt_cb.handle_of_h_r) + { + if ((conn_id = gatt_profile_find_conn_id_by_bd_addr(peer_bda)) != GATT_INVALID_CONN_ID) + { + UINT16_TO_STREAM (p, 1); + UINT16_TO_STREAM (p, 0xFFFF); + GATTS_HandleValueIndication (conn_id, + gatt_cb.handle_of_h_r, + GATT_SIZE_OF_SRV_CHG_HNDL_RANGE, + handle_range); + } + else + { + GATT_TRACE_ERROR2("Unable to find conn_id for %08x%04x ", + (peer_bda[0]<<24)+(peer_bda[1]<<16)+(peer_bda[2]<<8)+peer_bda[3], + (peer_bda[4]<<8)+peer_bda[5] ); + } + } +} + +/******************************************************************************* +** +** Function gatt_chk_srv_chg +** +** Description Check sending service chnaged Indication is required or not +** if required then send the Indication +** +** Returns void +** +*******************************************************************************/ +void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt) +{ + GATT_TRACE_DEBUG1("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed ); + + if (p_srv_chg_clt->srv_changed) + { + gatt_send_srv_chg_ind(p_srv_chg_clt->bda); + } + else + { + GATT_TRACE_DEBUG0("No need to send srv chg "); + } + +} + +/******************************************************************************* +** +** Function gatt_init_srv_chg +** +** Description This function is used to initialize the service changed +** attribute value +** +** Returns void +** +*******************************************************************************/ +void gatt_init_srv_chg (void) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG_RSP rsp; + BOOLEAN status; + UINT8 num_clients,i; + tGATTS_SRV_CHG srv_chg_clt; + + GATT_TRACE_DEBUG0("gatt_init_srv_chg"); + if (gatt_cb.cb_info.p_srv_chg_callback) + { + status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_NUM_CLENTS, NULL, &rsp); + + if (status && rsp.num_clients) + { + GATT_TRACE_DEBUG1("gatt_init_srv_chg num_srv_chg_clt_clients=%d", rsp.num_clients); + num_clients = rsp.num_clients; + i = 1; /* use one based index */ + while ((i <= num_clients) && status) + { + req.client_read_index = i; + if ((status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_CLENT, &req, &rsp))) + { + memcpy(&srv_chg_clt, &rsp.srv_chg ,sizeof(tGATTS_SRV_CHG)); + if (gatt_add_srv_chg_clt(&srv_chg_clt) == NULL) + { + GATT_TRACE_ERROR0("Unable to add a service change client"); + status = FALSE; + } + } + i++; + } + } + } + else + { + GATT_TRACE_DEBUG0("gatt_init_srv_chg callback not registered yet"); + } +} + +/******************************************************************************* +** +** Function gatt_proc_srv_chg +** +** Description This function is process the service changed request +** +** Returns void +** +*******************************************************************************/ +void gatt_proc_srv_chg (void) +{ + UINT8 start_idx, found_idx; + BD_ADDR bda; + BOOLEAN srv_chg_ind_pending=FALSE; + tGATT_TCB *p_tcb; + + GATT_TRACE_DEBUG0 ("gatt_proc_srv_chg"); + + if (gatt_cb.cb_info.p_srv_chg_callback && gatt_cb.handle_of_h_r) + { + gatt_set_srv_chg(); + start_idx =0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx)) + { + p_tcb = &gatt_cb.tcb[found_idx];; + srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); + + if (!srv_chg_ind_pending) + { + gatt_send_srv_chg_ind(bda); + } + else + { + GATT_TRACE_DEBUG0 ("discard srv chg - already has one in the queue"); + } + start_idx = ++found_idx; + } + } +} + +/******************************************************************************* +** +** Function gatt_set_ch_state +** +** Description This function set the ch_state in tcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state) +{ + if (p_tcb) + { + GATT_TRACE_DEBUG2 ("gatt_set_ch_state: old=%d new=%d", p_tcb->ch_state, ch_state); + p_tcb->ch_state = ch_state; + } +} + +/******************************************************************************* +** +** Function gatt_get_ch_state +** +** Description This function get the ch_state in tcb +** +** Returns none +** +*******************************************************************************/ +tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb) +{ + tGATT_CH_STATE ch_state = GATT_CH_CLOSE; + if (p_tcb) + { + GATT_TRACE_DEBUG1 ("gatt_get_ch_state: ch_state=%d", p_tcb->ch_state); + ch_state = p_tcb->ch_state; + } + return ch_state; +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_sr.c b/stack/gatt/gatt_sr.c new file mode 100644 index 0000000..ddecdb3 --- /dev/null +++ b/stack/gatt/gatt_sr.c @@ -0,0 +1,1486 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the GATT server functions + * + ******************************************************************************/ + +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE +#include +#include "gatt_int.h" +#include "l2c_api.h" + + +/******************************************************************************* +** +** Function gatt_sr_enqueue_cmd +** +** Description This function enqueue the request from client which needs a +** application response, and update the transaction ID. +** +** Returns void +** +*******************************************************************************/ +UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle) +{ + tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd; + UINT32 trans_id = 0; + + if ( (p_cmd->op_code == 0) || + (op_code == GATT_HANDLE_VALUE_CONF)) /* no pending request */ + { + if (op_code == GATT_CMD_WRITE || + op_code == GATT_SIGN_CMD_WRITE || + op_code == GATT_REQ_MTU || + op_code == GATT_HANDLE_VALUE_CONF) + { + trans_id = ++p_tcb->trans_id; + } + else + { + p_cmd->trans_id = ++p_tcb->trans_id; + p_cmd->op_code = op_code; + p_cmd->handle = handle; + p_cmd->status = GATT_NOT_FOUND; + p_tcb->trans_id %= GATT_TRANS_ID_MAX; + trans_id = p_cmd->trans_id; + } + } + + return trans_id; +} + +/******************************************************************************* +** +** Function gatt_sr_cmd_empty +** +** Description This function check the server command queue is empty or not. +** +** Returns TRUE if empty, FALSE if there is pending command. +** +*******************************************************************************/ +BOOLEAN gatt_sr_cmd_empty (tGATT_TCB *p_tcb) +{ + return(p_tcb->sr_cmd.op_code == 0); +} + +/******************************************************************************* +** +** Function gatt_dequeue_sr_cmd +** +** Description This function dequeue the request from command queue. +** +** Returns void +** +*******************************************************************************/ +void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb) +{ + /* Double check in case any buffers are queued */ + GATT_TRACE_DEBUG0("gatt_dequeue_sr_cmd" ); + if (p_tcb->sr_cmd.p_rsp_msg) + { + GATT_TRACE_ERROR1("free p_tcb->sr_cmd.p_rsp_msg = %d", p_tcb->sr_cmd.p_rsp_msg); + + GKI_freebuf (p_tcb->sr_cmd.p_rsp_msg); + } + + while (p_tcb->sr_cmd.multi_rsp_q.p_first) + GKI_freebuf (GKI_dequeue (&p_tcb->sr_cmd.multi_rsp_q)); + memset( &p_tcb->sr_cmd, 0, sizeof(tGATT_SR_CMD)); +} + +/******************************************************************************* +** +** Function process_read_multi_rsp +** +** Description This function check the read multiple response. +** +** Returns BOOLEAN if all replies have been received +** +*******************************************************************************/ +static BOOLEAN process_read_multi_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, + tGATTS_RSP *p_msg, UINT16 mtu) +{ + tGATTS_RSP *p_rsp; + UINT16 ii, total_len, len; + BT_HDR *p_buf = (BT_HDR *)GKI_getbuf((UINT16)sizeof(tGATTS_RSP)); + UINT8 *p; + BOOLEAN is_overflow = FALSE; + + GATT_TRACE_DEBUG2 ("process_read_multi_rsp status=%d mtu=%d", status, mtu); + + if (p_buf == NULL) + { + p_cmd->status = GATT_INSUF_RESOURCE; + return FALSE; + } + + /* Enqueue the response */ + memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); + GKI_enqueue (&p_cmd->multi_rsp_q, p_buf); + + p_cmd->status = status; + if (status == GATT_SUCCESS) + { + GATT_TRACE_DEBUG2 ("Multi read count=%d num_hdls=%d", + p_cmd->multi_rsp_q.count, p_cmd->multi_req.num_handles); + /* Wait till we get all the responses */ + if (p_cmd->multi_rsp_q.count == p_cmd->multi_req.num_handles) + { + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; + if ((p_buf = (BT_HDR *)GKI_getbuf(len)) == NULL) + { + p_cmd->status = GATT_INSUF_RESOURCE; + return(TRUE); + } + + memset(p_buf, 0, len); + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First byte in the response is the opcode */ + *p++ = GATT_RSP_READ_MULTI; + p_buf->len = 1; + + /* Now walk through the buffers puting the data into the response in order */ + for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) + { + if (ii==0) + { + p_rsp = (tGATTS_RSP *)GKI_getfirst (&p_cmd->multi_rsp_q); + } + else + { + p_rsp = (tGATTS_RSP *)GKI_getnext (p_rsp); + } + + if (p_rsp != NULL) + { + + total_len = (p_buf->len + p_rsp->attr_value.len); + + if (total_len > mtu) + { + /* just send the partial response for the overflow case */ + len = p_rsp->attr_value.len - (total_len - mtu); + is_overflow = TRUE; + GATT_TRACE_DEBUG2 ("multi read overflow available len=%d val_len=%d", len, p_rsp->attr_value.len ); + } + else + { + len = p_rsp->attr_value.len; + } + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) + { + memcpy (p, p_rsp->attr_value.value, len); + if (!is_overflow) + p += len; + p_buf->len += len; + } + else + { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + if (is_overflow) + break; + + } + else + { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + } /* loop through all handles*/ + + + /* Sanity check on the buffer length */ + if (p_buf->len == 0) + { + GATT_TRACE_ERROR0("process_read_multi_rsp - nothing found!!"); + p_cmd->status = GATT_NOT_FOUND; + GKI_freebuf (p_buf); + GATT_TRACE_DEBUG0(" GKI_freebuf (p_buf)"); + } + else if (p_cmd->p_rsp_msg != NULL) + { + GKI_freebuf (p_buf); + } + else + { + p_cmd->p_rsp_msg = p_buf; + } + + return(TRUE); + } + } + else /* any handle read exception occurs, return error */ + { + return(TRUE); + } + + /* If here, still waiting */ + return(FALSE); +} + +/******************************************************************************* +** +** Function gatt_sr_process_app_rsp +** +** Description This function checks whether the response message from application +** match any pending request or not. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, + UINT32 trans_id, UINT8 op_code, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS ret_code = GATT_SUCCESS; + + GATT_TRACE_DEBUG1("gatt_sr_process_app_rsp gatt_if=%d", gatt_if); + + gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE); + + if (op_code == GATT_REQ_READ_MULTI) + { + /* If no error and still waiting, just return */ + if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) + return(GATT_SUCCESS); + } + else + { + if (op_code == GATT_REQ_PREPARE_WRITE && status == GATT_SUCCESS) + gatt_sr_update_prep_cnt(p_tcb, gatt_if, TRUE, FALSE); + + if (op_code == GATT_REQ_EXEC_WRITE && status != GATT_SUCCESS) + gatt_sr_reset_cback_cnt(p_tcb); + + p_tcb->sr_cmd.status = status; + + if (gatt_sr_is_cback_cnt_zero(p_tcb) + && status == GATT_SUCCESS) + { + if (p_tcb->sr_cmd.p_rsp_msg == NULL) + { + p_tcb->sr_cmd.p_rsp_msg = attp_build_sr_msg (p_tcb, (UINT8)(op_code + 1), (tGATT_SR_MSG *)p_msg); + } + else + { + GATT_TRACE_ERROR0("Exception!!! already has respond message"); + } + } + } + if (gatt_sr_is_cback_cnt_zero(p_tcb)) + { + if ( (p_tcb->sr_cmd.status == GATT_SUCCESS) && (p_tcb->sr_cmd.p_rsp_msg) ) + { + ret_code = attp_send_sr_msg (p_tcb, p_tcb->sr_cmd.p_rsp_msg); + p_tcb->sr_cmd.p_rsp_msg = NULL; + } + else + { + ret_code = gatt_send_error_rsp (p_tcb, status, op_code, p_tcb->sr_cmd.handle, FALSE); + } + + gatt_dequeue_sr_cmd(p_tcb); + } + + GATT_TRACE_DEBUG1("gatt_sr_process_app_rsp ret_code=%d", ret_code); + + return ret_code; +} + +/******************************************************************************* +** +** Function gatt_process_exec_write_req +** +** Description This function is called to process the execute write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 *p = p_data, flag, i = 0; + UINT32 trans_id = 0; + BT_HDR *p_buf; + tGATT_IF gatt_if; + UINT16 conn_id; + + STREAM_TO_UINT8(flag, p); + + /* mask the flag */ + flag &= GATT_PREP_WRITE_EXEC; + + + /* no prep write is queued */ + if (!gatt_sr_is_prep_cnt_zero(p_tcb)) + { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); + gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + + for (i=0; iprep_cnt[i]) + { + gatt_if = (tGATT_IF) (i+1); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE_EXEC, + (tGATTS_DATA *)&flag); + p_tcb->prep_cnt[i]= 0; + } + } + } + else /* nothing needs to be executed , send response now */ + { + GATT_TRACE_ERROR0("gatt_process_exec_write_req: no prepare write pending"); + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_EXEC_WRITE, NULL)) != NULL) + { + attp_send_sr_msg (p_tcb, p_buf); + } + + } +} + +/******************************************************************************* +** +** Function gatt_process_read_multi_req +** +** Description This function is called to process the read multiple request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT32 trans_id; + UINT16 handle, ll = len; + UINT8 *p = p_data, i_rcb; + tGATT_STATUS err = GATT_SUCCESS; + UINT8 sec_flag, key_size; + tGATTS_RSP *p_msg; + + GATT_TRACE_DEBUG0("gatt_process_read_multi_req" ); + p_tcb->sr_cmd.multi_req.num_handles = 0; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID), + &sec_flag, + &key_size); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) + { + GATT_TRACE_DEBUG1("Conformance tst: forced err rspvofr ReadMultiple: error status=%d", gatt_cb.err_status); + + STREAM_TO_UINT16(handle, p); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + while (ll >= 2 && p_tcb->sr_cmd.multi_req.num_handles < GATT_MAX_READ_MULTI_HANDLES) + { + STREAM_TO_UINT16(handle, p); + + if ((i_rcb = gatt_sr_find_i_rcb_by_handle(handle)) < GATT_MAX_SR_PROFILES) + { + p_tcb->sr_cmd.multi_req.handles[p_tcb->sr_cmd.multi_req.num_handles++] = handle; + + /* check read permission */ + if ((err = gatts_read_attr_perm_check( gatt_cb.sr_reg[i_rcb].p_db, + FALSE, + handle, + sec_flag, + key_size)) + != GATT_SUCCESS) + { + GATT_TRACE_DEBUG1("read permission denied : 0x%02x", err); + break; + } + } + else + { + /* invalid handle */ + err = GATT_INVALID_HANDLE; + break; + } + ll -= 2; + } + + if (ll != 0) + { + GATT_TRACE_ERROR0("max attribute handle reached in ReadMultiple Request."); + } + + if (p_tcb->sr_cmd.multi_req.num_handles == 0) + err = GATT_INVALID_HANDLE; + + if (err == GATT_SUCCESS) + { + if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) + { + gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/ + + for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) + { + if ((p_msg = (tGATTS_RSP *)GKI_getbuf(sizeof(tGATTS_RSP))) != NULL) + { + memset(p_msg, 0, sizeof(tGATTS_RSP)) + ; + handle = p_tcb->sr_cmd.multi_req.handles[ll]; + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + + p_msg->attr_value.handle = handle; + err = gatts_read_attr_value_by_handle(p_tcb, + gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + 0, + p_msg->attr_value.value, + &p_msg->attr_value.len, + GATT_MAX_ATTR_LEN, + sec_flag, + key_size, + trans_id); + + if (err == GATT_SUCCESS) + { + gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if ,trans_id, op_code, GATT_SUCCESS, p_msg); + } + /* either not using or done using the buffer, release it now */ + GKI_freebuf(p_msg); + } + else + { + err = GATT_NO_RESOURCES; + gatt_dequeue_sr_cmd(p_tcb); + break; + } + } + } + else + err = GATT_NO_RESOURCES; + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (err != GATT_SUCCESS && err != GATT_PENDING && err != GATT_BUSY) + gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE); +} + +/******************************************************************************* +** +** Function gatt_build_primary_service_rsp +** +** Description Primamry service request processed internally. Theretically +** only deal with ReadByTypeVAlue and ReadByGroupType. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_tcb, + UINT8 op_code, UINT16 s_hdl, + UINT16 e_hdl, UINT8 *p_data, tBT_UUID value) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 handle_len =4, *p ; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list= &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv=NULL; + tBT_UUID *p_uuid; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + p_srv = p_list->p_first; + + while (p_srv) + { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + p_rcb->s_hdl >= s_hdl && + p_rcb->s_hdl <= e_hdl && + p_rcb->type == GATT_UUID_PRI_SERVICE) + { + p_uuid = gatts_get_service_uuid (p_rcb->p_db); + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) + handle_len = 4 + p_uuid->len; + + /* get the length byte in the repsonse */ + if (p_msg->offset ==0) + { + *p ++ = op_code + 1; + p_msg->len ++; + p_msg->offset = handle_len; + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) + { + *p ++ = (UINT8)p_msg->offset; /* length byte */ + p_msg->len ++; + } + } + + if (p_msg->len + p_msg->offset <= p_tcb->payload_size && + handle_len == p_msg->offset) + { + if (op_code != GATT_REQ_FIND_TYPE_VALUE || + gatt_uuid_compare(value, *p_uuid)) + { + UINT16_TO_STREAM(p, p_rcb->s_hdl); + + if (p_list->p_last_primary == p_srv && + p_list->p_last_primary == p_list->p_last) + { + GATT_TRACE_DEBUG0("Use 0xFFFF for the last primary attribute"); + UINT16_TO_STREAM(p, 0xFFFF); /* see GATT ERRATA 4065, 4063, ATT ERRATA 4062 */ + } + else + { + UINT16_TO_STREAM(p, p_rcb->e_hdl); + } + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) + gatt_build_uuid_to_stream(&p, *p_uuid); + + status = GATT_SUCCESS; + p_msg->len += p_msg->offset; + } + } + else + break; + } + p_srv = p_srv->p_next; + } + p_msg->offset = L2CAP_MIN_OFFSET; + + return status; +} + +/******************************************************************************* +** +** Function gatt_build_find_info_rsp +** +** Description fill the find information response information in the given +** buffer. +** +** Returns TRUE: if data filled sucessfully. +** FALSE: packet full, or format mismatch. +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_find_info_rsp(tGATT_SR_REG *p_rcb, BT_HDR *p_msg, UINT16 *p_len, + UINT16 s_hdl, UINT16 e_hdl) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 *p; + UINT16 len = *p_len; + tGATT_ATTR16 *p_attr = NULL; + UINT8 info_pair_len[2] = {4, 18}; + + if (!p_rcb->p_db || !p_rcb->p_db->p_attr_list) + return status; + + /* check the attribute database */ + p_attr = (tGATT_ATTR16 *) p_rcb->p_db->p_attr_list; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET + p_msg->len; + + while (p_attr) + { + if (p_attr->handle > e_hdl) + { + break; + } + + if (p_attr->handle >= s_hdl) + { + if (p_msg->offset == 0) + p_msg->offset = (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128) ? GATT_INFO_TYPE_PAIR_128 : GATT_INFO_TYPE_PAIR_16; + + if (len >= info_pair_len[p_msg->offset - 1]) + { + if (p_msg->offset == GATT_INFO_TYPE_PAIR_16 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) + { + UINT16_TO_STREAM(p, p_attr->handle); + UINT16_TO_STREAM(p, p_attr->uuid); + } + else if (p_msg->offset == GATT_INFO_TYPE_PAIR_128 && + p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128 ) + { + UINT16_TO_STREAM(p, p_attr->handle); + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *) p_attr)->uuid, LEN_UUID_128); + } + else + { + GATT_TRACE_ERROR0("format mismatch"); + status = GATT_NO_RESOURCES; + break; + /* format mismatch */ + } + p_msg->len += info_pair_len[p_msg->offset - 1]; + len -= info_pair_len[p_msg->offset - 1]; + status = GATT_SUCCESS; + + } + else + { + status = GATT_NO_RESOURCES; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + + *p_len = len; + return status; +} + +/******************************************************************************* +** +** Function gatts_internal_read_by_type_req +** +** Description check to see if the ReadByType request can be handled internally. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatts_validate_packet_format(UINT8 op_code, UINT16 *p_len, + UINT8 **p_data, tBT_UUID *p_uuid_filter, + UINT16 *p_s_hdl, UINT16 *p_e_hdl) +{ + tGATT_STATUS reason = GATT_SUCCESS; + UINT16 uuid_len, s_hdl = 0, e_hdl = 0; + UINT16 len = *p_len; + UINT8 *p = *p_data; + + if (len >= 4) + { + /* obtain starting handle, and ending handle */ + STREAM_TO_UINT16(s_hdl, p); + STREAM_TO_UINT16(e_hdl, p); + len -= 4; + + if (s_hdl > e_hdl || !GATT_HANDLE_IS_VALID(s_hdl) || !GATT_HANDLE_IS_VALID(e_hdl)) + { + reason = GATT_INVALID_HANDLE; + } + /* for these PDUs, uuid filter must present */ + else if (op_code == GATT_REQ_READ_BY_GRP_TYPE || + op_code == GATT_REQ_FIND_TYPE_VALUE || + op_code == GATT_REQ_READ_BY_TYPE) + { + if (len >= 2 && p_uuid_filter != NULL) + { + uuid_len = (op_code == GATT_REQ_FIND_TYPE_VALUE) ? 2 : len; + + /* parse uuid now */ + if (gatt_parse_uuid_from_cmd (p_uuid_filter, uuid_len, &p) == FALSE || + p_uuid_filter->len == 0) + { + GATT_TRACE_DEBUG0("UUID filter does not exsit"); + reason = GATT_INVALID_PDU; + } + else + len -= p_uuid_filter->len; + } + else + reason = GATT_INVALID_PDU; + } + } + + *p_data = p; + *p_len = len; + *p_s_hdl = s_hdl; + *p_e_hdl = e_hdl; + + return reason; +} + +/******************************************************************************* +** +** Function gatts_process_primary_service_req +** +** Description process ReadByGroupType/ReadByTypeValue request, for discover +** all primary services or discover primary service by UUID request. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_primary_service_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU; + UINT16 s_hdl = 0, e_hdl = 0; + tBT_UUID uuid, value, primary_service = {LEN_UUID_16, {GATT_UUID_PRI_SERVICE}}; + BT_HDR *p_msg = NULL; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) + { + if (gatt_uuid_compare(uuid, primary_service)) + { + if (op_code == GATT_REQ_FIND_TYPE_VALUE) + { + if (gatt_parse_uuid_from_cmd(&value, len, &p_data) == FALSE) + reason = GATT_INVALID_PDU; + } + + if (reason == GATT_SUCCESS) + { + if ((p_msg = (BT_HDR *)GKI_getbuf(msg_len)) == NULL) + { + GATT_TRACE_ERROR0("gatts_process_primary_service_req failed. no resources."); + reason = GATT_NO_RESOURCES; + } + else + { + memset(p_msg, 0, msg_len); + reason = gatt_build_primary_service_rsp (p_msg, p_tcb, op_code, s_hdl, e_hdl, p_data, value); + } + } + } + else + { + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) + { + reason = GATT_UNSUPPORT_GRP_TYPE; + GATT_TRACE_DEBUG1("unexpected ReadByGrpType Group: 0x%04x", uuid.uu.uuid16); + } + else + { + /* we do not support ReadByTypeValue with any non-primamry_service type */ + reason = GATT_NOT_FOUND; + GATT_TRACE_DEBUG1("unexpected ReadByTypeValue type: 0x%04x", uuid.uu.uuid16); + } + } + } + + if (reason != GATT_SUCCESS) + { + if (p_msg) GKI_freebuf(p_msg); + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } + else + attp_send_sr_msg(p_tcb, p_msg); + +} + +/******************************************************************************* +** +** Function gatts_process_find_info +** +** Description process find information request, for discover character +** descriptors. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU, *p; + UINT16 s_hdl = 0, e_hdl = 0, buf_len; + BT_HDR *p_msg = NULL; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list= &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv=NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, NULL, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) + { + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) + { + reason = GATT_NO_RESOURCES; + } + else + { + reason = GATT_NOT_FOUND; + + memset(p_msg, 0, buf_len); + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 2; + + buf_len = p_tcb->payload_size - 2; + + p_srv = p_list->p_first; + + while (p_srv) + { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) + { + reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl); + if (reason == GATT_NO_RESOURCES) + { + reason = GATT_SUCCESS; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + + if (reason != GATT_SUCCESS) + { + if (p_msg) GKI_freebuf(p_msg); + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } + else + attp_send_sr_msg(p_tcb, p_msg); + +} + +/******************************************************************************* +** +** Function gatts_process_mtu_req +** +** Description This function is called to process excahnge MTU request. +** Only used on LE. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu = 0; + UINT8 *p = p_data, i; + BT_HDR *p_buf; + UINT16 conn_id; + + STREAM_TO_UINT16 (mtu, p); + + /* BR/EDR conenction, send error response */ + if (p_tcb->att_lcid != L2CAP_ATT_CID) + { + gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, GATT_REQ_MTU, 0, FALSE); + } + else + { + /* mtu must be greater than default MTU which is 23/48 */ + if (mtu <= GATT_MAX_MTU_SIZE) + p_tcb->payload_size = mtu; + else + p_tcb->payload_size = GATT_MAX_MTU_SIZE; + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_MTU, (tGATT_SR_MSG *) &p_tcb->payload_size)) != NULL) + { + attp_send_sr_msg (p_tcb, p_buf); + + /* Notify all registered applicaiton with new MTU size. Us a transaction ID */ + /* of 0, as no response is allowed from applcations */ + + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (gatt_cb.cl_rcb[i].in_use ) + { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if); + gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, + (tGATTS_DATA *)&p_tcb->payload_size); + } + } + + } + } +} + +/******************************************************************************* +** +** Function gatts_process_read_by_type_req +** +** Description process Read By type request. +** This PDU can be used to perform: +** - read characteristic value +** - read characteristic descriptor value +** - discover characteristic +** - discover characteristic by UUID +** - relationship discovery +** +** Returns void +** +*******************************************************************************/ +void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tBT_UUID uuid; + tGATT_SR_REG *p_rcb; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET), + buf_len, + s_hdl, e_hdl, err_hdl = 0; + BT_HDR *p_msg = NULL; + tGATT_STATUS reason, ret; + UINT8 *p; + UINT8 sec_flag, key_size; + tGATT_SRV_LIST_INFO *p_list= &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv=NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) + { + GATT_TRACE_DEBUG1("Conformance tst: forced err rsp for ReadByType: error status=%d", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, s_hdl, FALSE); + + return; + } +#endif + + if (reason == GATT_SUCCESS) + { + if ((p_msg = (BT_HDR *)GKI_getbuf(msg_len)) == NULL) + { + GATT_TRACE_ERROR0("gatts_process_find_info failed. no resources."); + + reason = GATT_NO_RESOURCES; + } + else + { + memset(p_msg, 0, msg_len); + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + *p ++ = op_code + 1; + /* reserve length byte */ + p_msg->len = 2; + buf_len = p_tcb->payload_size - 2; + + reason = GATT_NOT_FOUND; + + p_srv = p_list->p_first; + + while (p_srv) + { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) + { + gatt_sr_get_sec_info(p_tcb->peer_bda, + (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID), + &sec_flag, + &key_size); + + ret = gatts_db_read_attr_value_by_type(p_tcb, + p_rcb->p_db, + op_code, + p_msg, + s_hdl, + e_hdl, + uuid, + &buf_len, + sec_flag, + key_size, + 0, + &err_hdl); + if (ret != GATT_NOT_FOUND) + { + reason = ret; + + if (ret == GATT_NO_RESOURCES) + reason = GATT_SUCCESS; + } + if (ret != GATT_SUCCESS && ret != GATT_NOT_FOUND) + { + s_hdl = err_hdl; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + if (reason != GATT_SUCCESS) + { + if (p_msg) GKI_freebuf(p_msg); + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_PENDING && reason != GATT_BUSY) + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } + else + attp_send_sr_msg(p_tcb, p_msg); + +} + +/******************************************************************************* +** +** Function gatts_process_write_req +** +** Description This function is called to process the write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATTS_DATA sr_data; + UINT32 trans_id; + tGATT_STATUS status; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id; + + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + switch (op_code) + { + case GATT_REQ_PREPARE_WRITE: + sr_data.write_req.is_prep = TRUE; + STREAM_TO_UINT16(sr_data.write_req.offset, p); + len -= 2; + /* fall through */ + case GATT_SIGN_CMD_WRITE: + if (op_code == GATT_SIGN_CMD_WRITE) + { + GATT_TRACE_DEBUG0("Write CMD with data sigining" ); + len -= GATT_AUTH_SIGN_LEN; + } + /* fall through */ + case GATT_CMD_WRITE: + case GATT_REQ_WRITE: + if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE) + sr_data.write_req.need_rsp = TRUE; + sr_data.write_req.handle = handle; + sr_data.write_req.len = len; + memcpy (sr_data.write_req.value, p, len); + break; + } + + gatt_sr_get_sec_info(p_tcb->peer_bda, + (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID), + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS) + { + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) + { + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE, + &sr_data); + + status = GATT_PENDING; + } + else + { + GATT_TRACE_ERROR0("max pending command, send error"); + status = GATT_BUSY; /* max pending command, application error */ + } + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (status != GATT_PENDING && status != GATT_BUSY && + (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE)) + { + gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + } + return; +} + +/******************************************************************************* +** +** Function gatts_process_read_req +** +** Description This function is called to process the read request +** from client. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, + UINT16 handle, UINT16 len, UINT8 *p_data) +{ + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATT_STATUS reason; + BT_HDR *p_msg = NULL; + UINT8 sec_flag, key_size, *p; + UINT16 offset = 0, value_len = 0; + + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) + { + GATT_TRACE_ERROR0("gatts_process_find_info failed. no resources."); + + reason = GATT_NO_RESOURCES; + } + else + { + if (op_code == GATT_REQ_READ_BLOB) + STREAM_TO_UINT16(offset, p_data); + + memset(p_msg, 0, buf_len); + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 1; + buf_len = p_tcb->payload_size - 1; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID), + &sec_flag, + &key_size); + + reason = gatts_read_attr_value_by_handle(p_tcb, + p_rcb->p_db, + op_code, + handle, + offset, + p, + &value_len, + buf_len, + sec_flag, + key_size, + 0); + + p_msg->len += value_len; + } + + if (reason != GATT_SUCCESS) + { + if (p_msg) GKI_freebuf(p_msg); + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_PENDING && reason != GATT_BUSY) + gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); + } + else + attp_send_sr_msg(p_tcb, p_msg); + +} + +/******************************************************************************* +** +** Function gatts_process_attribute_req +** +** Description This function is called to process the per attribute handle request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 handle; + UINT8 *p = p_data, i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + tGATT_STATUS status = GATT_INVALID_HANDLE; + tGATT_ATTR16 *p_attr; + + STREAM_TO_UINT16(handle, p); + len -= 2; + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) + { + GATT_TRACE_DEBUG1("Conformance tst: forced err rsp: error status=%d", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + if (GATT_HANDLE_IS_VALID(handle)) + { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) + { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) + { + p_attr = (tGATT_ATTR16 *)p_rcb->p_db->p_attr_list; + + while (p_attr) + { + if (p_attr->handle == handle) + { + switch (op_code) + { + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + gatts_process_read_req(p_tcb, p_rcb, op_code, handle, len, p); + break; + + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + case GATT_REQ_PREPARE_WRITE: + gatts_process_write_req(p_tcb, i, handle, op_code, len, p); + break; + default: + break; + } + status = GATT_SUCCESS; + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + break; + } + } + } + + if (status != GATT_SUCCESS && op_code != GATT_CMD_WRITE && op_code != GATT_SIGN_CMD_WRITE) + gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); +} + +/******************************************************************************* +** +** Function gatts_proc_srv_chg_ind_ack +** +** Description This function process the service changed indicaiton ACK +** +** Returns void +** +*******************************************************************************/ +static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb ) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG *p_buf = NULL; + + GATT_TRACE_DEBUG0("gatts_proc_srv_chg_ind_ack"); + + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) + { + GATT_TRACE_DEBUG0("NV update set srv chg = FALSE"); + p_buf->srv_changed = FALSE; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT,&req, NULL); + } +} + +/******************************************************************************* +** +** Function gatts_chk_pending_ind +** +** Description This function check any pending indication needs to be sent if +** there is a pending indication then sent the indication +** +** Returns void +** +*******************************************************************************/ +static void gatts_chk_pending_ind(tGATT_TCB *p_tcb ) +{ + tGATT_VALUE *p_buf = (tGATT_VALUE *)GKI_getfirst(&p_tcb->pending_ind_q); + GATT_TRACE_DEBUG0("gatts_chk_pending_ind"); + + if (p_buf ) + { + GATTS_HandleValueIndication (p_buf->conn_id, + p_buf->handle, + p_buf->len, + p_buf->value); + GKI_freebuf(GKI_remove_from_queue (&p_tcb->pending_ind_q, p_buf)); + } +} + +/******************************************************************************* +** +** Function gatts_proc_ind_ack +** +** Description This function process the Indication ack +** +** Returns TRUE continue to process the indication ack by the aaplication +** if the ACk is not a Service Changed Indication Ack +** +*******************************************************************************/ +static BOOLEAN gatts_proc_ind_ack(tGATT_TCB *p_tcb, UINT16 ack_handle) +{ + BOOLEAN continue_processing = TRUE; + + GATT_TRACE_DEBUG1 ("gatts_proc_ind_ack ack handle=%d", ack_handle); + + if (ack_handle == gatt_cb.handle_of_h_r) + { + gatts_proc_srv_chg_ind_ack(p_tcb); + /* there is no need to inform the application since srv chg is handled internally by GATT */ + continue_processing = FALSE; + } + + gatts_chk_pending_ind(p_tcb); + return continue_processing; +} + +/******************************************************************************* +** +** Function gatts_process_value_conf +** +** Description This function is called to process the handle value confirmation. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) +{ + UINT16 handle = p_tcb->indicate_handle; + UINT32 trans_id; + UINT8 i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + BOOLEAN continue_processing; + UINT16 conn_id; + + btu_stop_timer (&p_tcb->conf_timer_ent); + if (GATT_HANDLE_IS_VALID(handle)) + { + p_tcb->indicate_handle = 0; + continue_processing = gatts_proc_ind_ack(p_tcb, handle); + + if (continue_processing) + { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) + { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) + { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_CONF, (tGATTS_DATA *)&handle); + } + } + } + } + else + { + GATT_TRACE_ERROR0("unexpected handle value confirmation"); + } +} + +/******************************************************************************* +** +** Function gatt_server_handle_client_req +** +** Description This function is called to handle the client requests to +** server. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + /* there is pending command, discard this one */ + if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) + return; + + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not include op code */ + if (len >= p_tcb->payload_size) + { + GATT_TRACE_ERROR2("server receive invalid PDU size:%d pdu size:%d", len + 1, p_tcb->payload_size ); + /* for invalid request expecting response, send it now */ + if (op_code != GATT_CMD_WRITE && + op_code != GATT_SIGN_CMD_WRITE && + op_code != GATT_HANDLE_VALUE_CONF) + { + gatt_send_error_rsp (p_tcb, GATT_INVALID_PDU, op_code, 0, FALSE); + } + /* otherwise, ignore the pkt */ + } + else + { + switch (op_code) + { + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + gatts_process_primary_service_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_FIND_INFO:/* discover char descrptor */ + gatts_process_find_info(p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + /* discover characteristic, discover char by UUID */ + gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); + break; + + + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + case GATT_REQ_PREPARE_WRITE: + gatts_process_attribute_req (p_tcb, op_code, len, p_data); + break; + + case GATT_HANDLE_VALUE_CONF: + gatts_process_value_conf (p_tcb, op_code); + break; + + case GATT_REQ_MTU: + gatts_process_mtu_req (p_tcb, len, p_data); + break; + + case GATT_REQ_EXEC_WRITE: + gatt_process_exec_write_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_MULTI: + gatt_process_read_multi_req (p_tcb, op_code, len, p_data); + break; + + default: + break; + } + } +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/gatt/gatt_utils.c b/stack/gatt/gatt_utils.c new file mode 100644 index 0000000..bd3aaf6 --- /dev/null +++ b/stack/gatt/gatt_utils.c @@ -0,0 +1,2600 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT utility functions + * + ******************************************************************************/ +#include "bt_target.h" + +#if BLE_INCLUDED == TRUE + #include + #include "stdio.h" + #include "gki.h" + + #include "l2cdefs.h" + #include "gatt_int.h" + #include "gatt_api.h" + #include "gattdefs.h" + #include "sdp_api.h" + #include "btm_int.h" +/* check if [x, y] and [a, b] have overlapping range */ + #define GATT_VALIDATE_HANDLE_RANGE(x, y, a, b) (y >= a && x <= b) + + #define GATT_GET_NEXT_VALID_HANDLE(x) (((x)/10 + 1) * 10) + +const char * const op_code_name[] = +{ + "UNKNOWN", + "ATT_RSP_ERROR", + "ATT_REQ_MTU", + "ATT_RSP_MTU", + "ATT_REQ_READ_INFO", + "ATT_RSP_READ_INFO", + "ATT_REQ_FIND_TYPE_VALUE", + "ATT_RSP_FIND_TYPE_VALUE", + "ATT_REQ_READ_BY_TYPE", + "ATT_RSP_READ_BY_TYPE", + "ATT_REQ_READ", + "ATT_RSP_READ", + "ATT_REQ_READ_BLOB", + "ATT_RSP_READ_BLOB", + "GATT_REQ_READ_MULTI", + "GATT_RSP_READ_MULTI", + "GATT_REQ_READ_BY_GRP_TYPE", + "GATT_RSP_READ_BY_GRP_TYPE", + "ATT_REQ_WRITE", + "ATT_RSP_WRITE", + "ATT_CMD_WRITE", + "ATT_SIGN_CMD_WRITE", + "ATT_REQ_PREPARE_WRITE", + "ATT_RSP_PREPARE_WRITE", + "ATT_REQ_EXEC_WRITE", + "ATT_RSP_EXEC_WRITE", + "Reserved", + "ATT_HANDLE_VALUE_NOTIF", + "Reserved", + "ATT_HANDLE_VALUE_IND", + "ATT_HANDLE_VALUE_CONF", + "ATT_OP_CODE_MAX" +}; + +static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +/******************************************************************************* +** +** Function gatt_free_pending_ind +** +** Description Free all pending indications +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_ind(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG0("gatt_free_pending_ind"); + /* release all queued indications */ + while (p_tcb->pending_ind_q.p_first) + GKI_freebuf (GKI_dequeue (&p_tcb->pending_ind_q)); +} + +/******************************************************************************* +** +** Function gatt_delete_dev_from_srv_chg_clt_list +** +** Description Delete a device from the service changed client lit +** +** Returns None +** +*******************************************************************************/ +void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr) +{ + tGATTS_SRV_CHG *p_buf; + tGATTS_SRV_CHG_REQ req; + + GATT_TRACE_DEBUG0 ("gatt_delete_dev_from_srv_chg_clt_list"); + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) + { + if (gatt_cb.cb_info.p_srv_chg_callback) + { + /* delete from NV */ + memcpy(req.srv_chg.bda, bd_addr, BD_ADDR_LEN); + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_REMOVE_CLIENT,&req, NULL); + } + GKI_freebuf (GKI_remove_from_queue (&gatt_cb.srv_chg_clt_q, p_buf)); + } + +} + +/******************************************************************************* +** +** Function gatt_set_srv_chg +** +** Description Set the service changed flag to TRUE +** +** Returns None +** +*******************************************************************************/ +void gatt_set_srv_chg(void) +{ + tGATTS_SRV_CHG *p_buf = (tGATTS_SRV_CHG *)GKI_getfirst(&gatt_cb.srv_chg_clt_q); + tGATTS_SRV_CHG_REQ req; + + GATT_TRACE_DEBUG0 ("gatt_set_srv_chg"); + while (p_buf) + { + GATT_TRACE_DEBUG0 ("found a srv_chg clt"); + if (!p_buf->srv_changed) + { + GATT_TRACE_DEBUG0 ("set srv_changed to TRUE"); + p_buf->srv_changed= TRUE; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT,&req, NULL); + } + p_buf = (tGATTS_SRV_CHG *)GKI_getnext(p_buf); + } +} + +/******************************************************************************* +** +** Function gatt_sr_is_new_srv_chg +** +** Description Find the app id in on the new service changed list +** +** Returns Pointer to the found new service changed item othwerwise NULL +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + tGATTS_HNDL_RANGE *p; + tGATTS_PENDING_NEW_SRV_START *p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getfirst(&gatt_cb.pending_new_srv_start_q); + + while (p_buf != NULL) + { + p = p_buf->p_new_srv_start; + if ( gatt_uuid_compare (*p_app_uuid128, p->app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p->svc_uuid) + && (svc_inst == p->svc_inst) ) + { + GATT_TRACE_DEBUG0 ("gatt_sr_is_new_srv_chg: Yes"); + break; + } + p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getnext(p_buf); + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_ind +** +** Description Add a pending indication +** +** Returns Pointer to the current pending indication buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind) +{ + tGATT_VALUE *p_buf; + GATT_TRACE_DEBUG0 ("gatt_add_pending_ind"); + if ((p_buf = (tGATT_VALUE *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL) + { + GATT_TRACE_DEBUG0 ("enqueue a pending indication"); + memcpy(p_buf, p_ind, sizeof(tGATT_VALUE)); + GKI_enqueue (&p_tcb->pending_ind_q, p_buf); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_new_srv_start +** +** Description Add a pending new srv start to the new service start queue +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start(tGATTS_HNDL_RANGE *p_new_srv_start) +{ + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_DEBUG0 ("gatt_add_pending_new_srv_start"); + if ((p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getbuf((UINT16)sizeof(tGATTS_PENDING_NEW_SRV_START))) != NULL) + { + GATT_TRACE_DEBUG0 ("enqueue a new pending new srv start"); + p_buf->p_new_srv_start = p_new_srv_start; + GKI_enqueue (&gatt_cb.pending_new_srv_start_q, p_buf); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_srv_chg_clt +** +** Description Add a service chnage client to the service change client queue +** +** Returns Pointer to the service change client buffer; Null no buffer available +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg) +{ + tGATTS_SRV_CHG *p_buf; + GATT_TRACE_DEBUG0 ("gatt_add_srv_chg_clt"); + if ((p_buf = (tGATTS_SRV_CHG *)GKI_getbuf((UINT16)sizeof(tGATTS_SRV_CHG))) != NULL) + { + GATT_TRACE_DEBUG0 ("enqueue a srv chg client"); + memcpy(p_buf, p_srv_chg, sizeof(tGATTS_SRV_CHG)); + GKI_enqueue (&gatt_cb.srv_chg_clt_q, p_buf); + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_alloc_hdl_buffer +** +** Description Allocate a handle buufer +** +** Returns Pointer to the allocated buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void) +{ + UINT8 i; + tGATT_CB *p_cb = &gatt_cb; + tGATT_HDL_LIST_ELEM * p_elem= &p_cb->hdl_list[0]; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_elem ++) + { + if (!p_cb->hdl_list[i].in_use) + { + memset(p_elem, 0, sizeof(tGATT_HDL_LIST_ELEM)); + p_elem->in_use = TRUE; + return p_elem; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_handle +** +** Description Find handle range buffer by service handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle) +{ + tGATT_HDL_LIST_INFO *p_list_info= &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) + { + if (p_list->in_use && p_list->asgn_range.s_handle == handle) + { + return(p_list); + } + p_list = p_list->p_next; + } + return NULL; +} +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_app_id +** +** Description Find handle range buffer by app ID, service and service instance ID. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, + tBT_UUID *p_svc_uuid, + UINT16 svc_inst) +{ + tGATT_HDL_LIST_INFO *p_list_info= &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) + { + if ( gatt_uuid_compare (*p_app_uuid128, p_list->asgn_range.app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p_list->asgn_range.svc_uuid) + && (svc_inst == p_list->asgn_range.svc_inst) ) + { + GATT_TRACE_DEBUG0 ("Already allocated handles for this service before!!"); + return(p_list); + } + p_list = p_list->p_next; + } + return NULL; +} +/******************************************************************************* +** +** Function gatt_free_hdl_buffer +** +** Description free a handle buffer +** +** Returns None +** +*******************************************************************************/ +void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p) +{ + + if (p) + { + while (p->svc_db.svc_buffer.p_first) + GKI_freebuf (GKI_dequeue (&p->svc_db.svc_buffer)); + memset(p, 0, sizeof(tGATT_HDL_LIST_ELEM)); + } +} +/******************************************************************************* +** +** Function gatt_free_srvc_db_buffer_app_id +** +** Description free the service attribute database buffers by the owner of the +** service app ID. +** +** Returns None +** +*******************************************************************************/ +void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id) +{ + tGATT_HDL_LIST_ELEM *p_elem = &gatt_cb.hdl_list[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_elem ++) + { + if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) + { + while (p_elem->svc_db.svc_buffer.p_first) + GKI_freebuf (GKI_dequeue (&p_elem->svc_db.svc_buffer)); + + p_elem->svc_db.mem_free = 0; + p_elem->svc_db.p_attr_list = p_elem->svc_db.p_free_mem = NULL; + } + } +} +/******************************************************************************* +** +** Function gatt_is_last_attribute +** +** Description Check this is the last attribute of the specified value or not +** +** Returns TRUE - yes this is the last attribute +** +*******************************************************************************/ +BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value) +{ + tGATT_SRV_LIST_ELEM *p_srv= p_start->p_next; + BOOLEAN is_last_attribute = TRUE; + tGATT_SR_REG *p_rcb = NULL; + tBT_UUID *p_svc_uuid; + + p_list->p_last_primary = NULL; + + while (p_srv) + { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + p_svc_uuid = gatts_get_service_uuid (p_rcb->p_db); + + if (gatt_uuid_compare(value, *p_svc_uuid)) + { + is_last_attribute = FALSE; + break; + + } + p_srv = p_srv->p_next; + } + + return is_last_attribute; + +} + +/******************************************************************************* +** +** Function gatt_update_last_pri_srv_info +** +** Description Update the the last primary info for the service list info +** +** Returns None +** +*******************************************************************************/ +void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list) +{ + tGATT_SRV_LIST_ELEM *p_srv= p_list->p_first; + + p_list->p_last_primary = NULL; + + while (p_srv) + { + if (p_srv->is_primary) + { + p_list->p_last_primary = p_srv; + } + p_srv = p_srv->p_next; + } + +} +/******************************************************************************* +** +** Function gatts_update_srv_list_elem +** +** Description update an element in the service list. +** +** Returns None. +** +*******************************************************************************/ +void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary) +{ + gatt_cb.srv_list[i_sreg].in_use = TRUE; + gatt_cb.srv_list[i_sreg].i_sreg = i_sreg; + gatt_cb.srv_list[i_sreg].s_hdl = gatt_cb.sr_reg[i_sreg].s_hdl; + gatt_cb.srv_list[i_sreg].is_primary = is_primary; + + return; +} +/******************************************************************************* +** +** Function gatt_add_a_srv_to_list +** +** Description add an service to the list in ascending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new) +{ + tGATT_SRV_LIST_ELEM *p_old; + + if (!p_new) + { + GATT_TRACE_DEBUG0("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) + { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } + else + { + p_old = p_list->p_first; + while (1) + { + if (p_old == NULL) + { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + break; + } + else + { + if (p_new->s_hdl < p_old->s_hdl) + { + /* if not the first in list */ + if (p_old->p_prev != NULL) + p_old->p_prev->p_next = p_new; + else + p_list->p_first = p_new; + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_a_srv_from_list +** +** Description Remove a service from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) + { + GATT_TRACE_DEBUG0("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) + { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) + p_remove->p_next->p_prev = NULL; + } + else if (p_remove->p_next == NULL) + { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } + else + { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_add_an_item_to_list +** +** Description add an service handle range to the list in decending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new) +{ + tGATT_HDL_LIST_ELEM *p_old; + if (!p_new) + { + GATT_TRACE_DEBUG0("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) + { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } + else + { + p_old = p_list->p_first; + while (1) + { + if (p_old == NULL) + { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + + break; + + } + else + { + if (p_new->asgn_range.s_handle > p_old->asgn_range.s_handle) + { + if (p_old == p_list->p_first) + p_list->p_first = p_new; + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + + + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_an_item_from_list +** +** Description Remove an service handle range from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) + { + GATT_TRACE_DEBUG0("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) + { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) + p_remove->p_next->p_prev = NULL; + } + else if (p_remove->p_next == NULL) + { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } + else + { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_find_the_connected_bda +** +** Description This function find the connected bda +** +** Returns TRUE if found +** +*******************************************************************************/ +BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx) +{ + UINT8 i; + BOOLEAN found = FALSE; + GATT_TRACE_DEBUG1("gatt_find_the_connected_bda start_idx=%d",start_idx); + + for (i = start_idx ; i < GATT_MAX_PHY_CHANNEL; i ++) + { + if (gatt_cb.tcb[i].in_use) + { + memcpy( bda, gatt_cb.tcb[i].peer_bda, BD_ADDR_LEN); + *p_found_idx = i; + found = TRUE; + GATT_TRACE_DEBUG6("gatt_find_the_connected_bda bda :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + break; + } + } + GATT_TRACE_DEBUG2("gatt_find_the_connected_bda found=%d found_idx=%d", found, i); + return found; +} + + + +/******************************************************************************* +** +** Function gatt_is_srv_chg_ind_pending +** +** Description Check whether a service chnaged is in the indication pending queue +** or waiting for an Ack already +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb) +{ + tGATT_VALUE *p_buf = (tGATT_VALUE *)GKI_getfirst(&p_tcb->pending_ind_q); + BOOLEAN srv_chg_ind_pending = FALSE; + + GATT_TRACE_DEBUG1("gatt_is_srv_chg_ind_pending is_queue_empty=%d", GKI_queue_is_empty(&p_tcb->pending_ind_q) ); + + if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) + { + srv_chg_ind_pending = TRUE; + } + else + { + while (p_buf) + { + if (p_buf->handle == gatt_cb.handle_of_h_r) + { + srv_chg_ind_pending = TRUE; + break; + } + p_buf = (tGATT_VALUE *)GKI_getnext(p_buf); + } + } + + GATT_TRACE_DEBUG1("srv_chg_ind_pending = %d", srv_chg_ind_pending); + return srv_chg_ind_pending; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_in_the_srv_chg_clt_list +** +** Description This function check the specified bda is in the srv chg clinet list or not +** +** Returns pointer to the found elemenet otherwise NULL +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda) +{ + tGATTS_SRV_CHG *p_buf = (tGATTS_SRV_CHG *)GKI_getfirst(&gatt_cb.srv_chg_clt_q); + + GATT_TRACE_DEBUG6("gatt_is_bda_in_the_srv_chg_clt_list :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + while (p_buf != NULL) + { + if (!memcmp( bda, p_buf->bda, BD_ADDR_LEN)) + { + GATT_TRACE_DEBUG0("bda is in the srv chg clt list"); + break; + } + p_buf = (tGATTS_SRV_CHG *)GKI_getnext(p_buf); + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_connected +** +** Description +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +BOOLEAN gatt_is_bda_connected(BD_ADDR bda) +{ + UINT8 i = 0; + BOOLEAN connected=FALSE; + + for ( i=0; i < GATT_MAX_PHY_CHANNEL; i ++) + { + if (gatt_cb.tcb[i].in_use && + !memcmp(gatt_cb.tcb[i].peer_bda, bda, BD_ADDR_LEN)) + { + connected = TRUE; + break; + } + } + return connected; +} + +/******************************************************************************* +** +** Function gatt_find_i_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_by_addr(BD_ADDR bda) +{ + UINT8 i = 0, j = GATT_INDEX_INVALID; + + for ( ; i < GATT_MAX_PHY_CHANNEL; i ++) + { + if (!memcmp(gatt_cb.tcb[i].peer_bda, bda, BD_ADDR_LEN)) + { + j = i; + break; + } + } + return j; +} + + +/******************************************************************************* +** +** Function gatt_get_tcb_by_idx +** +** Description The function get TCB using the TCB index +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB * gatt_get_tcb_by_idx(UINT8 tcb_idx) +{ + tGATT_TCB *p_tcb = NULL; + + if ( (tcb_idx < GATT_MAX_PHY_CHANNEL) && gatt_cb.tcb[tcb_idx].in_use) + p_tcb = &gatt_cb.tcb[tcb_idx]; + + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_find_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return pointer. +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB * gatt_find_tcb_by_addr(BD_ADDR bda) +{ + tGATT_TCB *p_tcb = NULL; + UINT8 i = 0; + + if ((i = gatt_find_i_tcb_by_addr(bda)) != GATT_INDEX_INVALID) + p_tcb = &gatt_cb.tcb[i]; + + return p_tcb; +} +/******************************************************************************* +** +** Function gatt_find_i_tcb_free +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_free(void) +{ + UINT8 i = 0, j = GATT_INDEX_INVALID; + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i ++) + { + if (!gatt_cb.tcb[i].in_use) + { + j = i; + break; + } + } + return j; +} +/******************************************************************************* +** +** Function gatt_allocate_tcb_by_bdaddr +** +** Description The function locate or allocate new tcb entry for matching bda. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB * gatt_allocate_tcb_by_bdaddr(BD_ADDR bda) +{ + UINT8 i = 0; + BOOLEAN allocated = FALSE; + tGATT_TCB *p_tcb = NULL; + + /* search for existing tcb with matching bda */ + i = gatt_find_i_tcb_by_addr(bda); + /* find free tcb */ + if (i == GATT_INDEX_INVALID) + { + i = gatt_find_i_tcb_free(); + allocated = TRUE; + } + if (i != GATT_INDEX_INVALID) + { + p_tcb = &gatt_cb.tcb[i]; + + if (allocated) + { + memset(p_tcb, 0, sizeof(tGATT_TCB)); + p_tcb->in_use = TRUE; + p_tcb->tcb_idx = i; + } + memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN); + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_convert_uuid16_to_uuid128 +** +** Description Convert a 16 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT16_TO_STREAM(p, uuid_16); +} + +/******************************************************************************* +** +** Function gatt_uuid_compare +** +** Description Compare two UUID to see if they are the same. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN gatt_uuid_compare (tBT_UUID src, tBT_UUID tar) +{ + UINT8 su[LEN_UUID_128], tu[LEN_UUID_128]; + UINT8 *ps, *pt; + + /* any of the UUID is unspecified */ + if (src.len == 0 || tar.len == 0) + { + return TRUE; + } + + /* If both are 16-bit, we can do a simple compare */ + if (src.len == 2 && tar.len == 2) + { + return src.uu.uuid16 == tar.uu.uuid16; + } + + /* One or both of the UUIDs is 128-bit */ + if (src.len == LEN_UUID_16) + { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(su, src.uu.uuid16); + ps = su; + } + else + ps = src.uu.uuid128; + + if (tar.len == LEN_UUID_16) + { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(tu, tar.uu.uuid16); + pt = tu; + } + else + pt = tar.uu.uuid128; + + return(memcmp(ps, pt, LEN_UUID_128) == 0); +} + +/******************************************************************************* +** +** Function gatt_build_uuid_to_stream +** +** Description Add UUID into stream. +** +** Returns UUID length. +** +*******************************************************************************/ +UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid) +{ + UINT8 *p = *p_dst; + UINT8 len = 0; + + if (uuid.len == LEN_UUID_16) + { + UINT16_TO_STREAM (p, uuid.uu.uuid16); + len = LEN_UUID_16; + } + else if (uuid.len == LEN_UUID_128) + { + ARRAY_TO_STREAM (p, uuid.uu.uuid128, LEN_UUID_128); + len = LEN_UUID_128; + } + + *p_dst = p; + return len; +} + +/******************************************************************************* +** +** Function gatt_parse_uuid_from_cmd +** +** Description Convert a 128 bits UUID into a 16 bits UUID. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid_rec, UINT16 uuid_size, UINT8 **p_data) +{ + BOOLEAN is_base_uuid, ret = TRUE; + UINT8 xx; + UINT8 *p_uuid = *p_data; + + memset(p_uuid_rec, 0, sizeof(tBT_UUID)); + + switch (uuid_size) + { + case LEN_UUID_16: + p_uuid_rec->len = uuid_size; + STREAM_TO_UINT16 (p_uuid_rec->uu.uuid16, p_uuid); + *p_data += LEN_UUID_16; + break; + + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) + { + if (p_uuid[xx] != base_uuid[xx]) + { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) + { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) + { + p_uuid += (LEN_UUID_128 - 4); + p_uuid_rec->len = LEN_UUID_16; + STREAM_TO_UINT16(p_uuid_rec->uu.uuid16, p_uuid); + } + else + is_base_uuid = FALSE; + } + if (!is_base_uuid) + { + p_uuid_rec->len = LEN_UUID_128; + memcpy(p_uuid_rec->uu.uuid128, p_uuid, LEN_UUID_128); + } + *p_data += LEN_UUID_128; + break; + + case 0: + default: + if (uuid_size != 0) ret = FALSE; + GATT_TRACE_WARNING0("gatt_parse_uuid_from_cmd invalid uuid size"); + break; + } + + return( ret); +} + +/******************************************************************************* +** +** Function gatt_start_rsp_timer +** +** Description Start a wait_for_response timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_rsp_timer(tGATT_TCB *p_tcb) +{ + p_tcb->rsp_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + btu_start_timer (&p_tcb->rsp_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + GATT_WAIT_FOR_RSP_TOUT); +} +/******************************************************************************* +** +** Function gatt_start_conf_timer +** +** Description Start a wait_for_confirmation timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_conf_timer(tGATT_TCB *p_tcb) +{ + p_tcb->conf_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + GATT_WAIT_FOR_RSP_TOUT); +} +/******************************************************************************* +** +** Function gatt_start_ind_ack_timer +** +** Description start the application ack timer +** +** Returns void +** +*******************************************************************************/ +void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb) +{ + p_tcb->ind_ack_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + /* start notification cache timer */ + btu_start_timer (&p_tcb->ind_ack_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_IND_ACK, + GATT_WAIT_FOR_RSP_TOUT); + +} +/******************************************************************************* +** +** Function gatt_rsp_timeout +** +** Description Called when GATT wait for ATT command response timer expires +** +** Returns void +** +*******************************************************************************/ +void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle) +{ + GATT_TRACE_WARNING0("gatt_rsp_timeout disconnecting..."); + gatt_disconnect (((tGATT_TCB *)p_tle->param)->peer_bda); +} + +/******************************************************************************* +** +** Function gatt_ind_ack_timeout +** +** Description Called when GATT wait for ATT handle confirmation timeout +** +** Returns void +** +*******************************************************************************/ +void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_TCB * p_tcb = (tGATT_TCB *)p_tle->param; + + GATT_TRACE_WARNING0("gatt_ind_ack_timeout send ack now"); + + if (p_tcb != NULL) + p_tcb->ind_count = 0; + + attp_send_cl_msg(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL); +} +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns GATT_MAX_SR_PROFILES if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle) +{ + UINT8 i_rcb = 0; + + for ( ; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++) + { + if (gatt_cb.sr_reg[i_rcb].in_use && + gatt_cb.sr_reg[i_rcb].s_hdl <= handle && + gatt_cb.sr_reg[i_rcb].e_hdl >= handle ) + { + break; + } + } + return i_rcb; +} + +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns 0 if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + UINT8 i_rcb = 0; + tGATT_SR_REG *p_sreg; + tBT_UUID *p_this_uuid; + + for (i_rcb = 0, p_sreg = gatt_cb.sr_reg; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++, p_sreg++) + { + if ( p_sreg->in_use ) + { + p_this_uuid = gatts_get_service_uuid (p_sreg->p_db); + + if (p_this_uuid && + gatt_uuid_compare (*p_app_uuid128, p_sreg->app_uuid ) && + gatt_uuid_compare (*p_svc_uuid, *p_this_uuid) && + (svc_inst == p_sreg->service_instance)) + { + GATT_TRACE_ERROR0 ("Active Service Found "); + gatt_dbg_display_uuid(*p_svc_uuid); + + break; + } + } + } + return i_rcb; +} +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns 0 if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list ) +{ + UINT8 ii = 0; + tGATT_SR_REG *p_sreg = NULL; + + /*this is a new application servoce start */ + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) + { + if (!p_sreg->in_use) + { + memset (p_sreg, 0, sizeof(tGATT_SR_REG)); + + p_sreg->in_use = TRUE; + memcpy (&p_sreg->app_uuid, &p_list->asgn_range.app_uuid128, sizeof(tBT_UUID)); + + p_sreg->service_instance = p_list->asgn_range.svc_inst; + p_sreg->type = p_list->asgn_range.is_primary ? GATT_UUID_PRI_SERVICE: GATT_UUID_SEC_SERVICE; + p_sreg->s_hdl = p_list->asgn_range.s_handle; + p_sreg->e_hdl = p_list->asgn_range.e_handle; + //p_sreg->sr_cb = *p_cback; + p_sreg->p_db = &p_list->svc_db; + + GATT_TRACE_DEBUG1 ("total GKI buffer in db [%d]",p_sreg->p_db->svc_buffer.count); + break; + } + } + + return ii; +} +/******************************************************************************* +** +** Function gatt_sr_get_sec_info +** +** Description Get the security flag and key size information for the peer +** device. +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_get_sec_info(BD_ADDR rem_bda, BOOLEAN le_conn, UINT8 *p_sec_flag, UINT8 *p_key_size) +{ + UINT8 sec_flag = 0; + + BTM_GetSecurityFlags(rem_bda, &sec_flag); + + sec_flag &= (GATT_SEC_FLAG_LKEY_UNAUTHED | GATT_SEC_FLAG_LKEY_AUTHED | GATT_SEC_FLAG_ENCRYPTED); + + *p_key_size = btm_ble_read_sec_key_size(rem_bda); + *p_sec_flag = sec_flag; +} +/******************************************************************************* +** +** Function gatt_sr_send_req_callback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_send_req_callback(UINT16 conn_id, + UINT32 trans_id, + tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if (!p_reg ) + { + GATT_TRACE_ERROR0 ("p_reg not found discard request"); + return; + } + + if ( p_reg->in_use && + p_reg->app_cb.p_req_cb) + { + (*p_reg->app_cb.p_req_cb)(conn_id, trans_id, type, p_data); + } + else + { + GATT_TRACE_WARNING1("Call back not found for application conn_id=%d", conn_id); + } + +} + +/******************************************************************************* +** +** Function gatt_send_error_rsp +** +** Description This function sends an error response. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_send_error_rsp (tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, + UINT16 handle, BOOLEAN deq) +{ + tGATT_ERROR error; + tGATT_STATUS status; + BT_HDR *p_buf; + + error.cmd_code = op_code; + error.reason = err_code; + error.handle =handle; + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_ERROR, (tGATT_SR_MSG *)&error)) != NULL) + { + status = attp_send_sr_msg (p_tcb, p_buf); + } + else + status = GATT_INSUF_RESOURCE; + + if (deq) + gatt_dequeue_sr_cmd(p_tcb); + + return status; +} + + +/******************************************************************************* +** +** Function gatt_add_sdp_record +** +** Description This function add a SDP record for a GATT primary service +** +** Returns 0 if error else sdp handle for the record. +** +*******************************************************************************/ +UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[2]; + UINT32 sdp_handle; + UINT16 list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT8 buff[60]; + UINT8 *p = buff; + + GATT_TRACE_DEBUG2("gatt_add_sdp_record s_hdl=0x%x s_hdl=0x%x",start_hdl, end_hdl); + + if ((sdp_handle = SDP_CreateRecord()) == 0) + return 0; + + switch (p_uuid->len) + { + case LEN_UUID_16: + SDP_AddServiceClassIdList(sdp_handle, 1, &p_uuid->uu.uuid16); + break; + case LEN_UUID_128: + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM (p, p_uuid->uu.uuid128, LEN_UUID_128); + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff); + break; + + default: + GATT_TRACE_ERROR1("inavlid UUID len=%d", p_uuid->len); + SDP_DeleteRecord(sdp_handle); + return 0; + break; + } + + /*** Fill out the protocol element sequence for SDP ***/ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 1; + proto_elem_list[0].params[0] = BT_PSM_ATT; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_ATT; + proto_elem_list[1].num_params = 2; + proto_elem_list[1].params[0] = start_hdl; + proto_elem_list[1].params[1] = end_hdl; + + SDP_AddProtocolList(sdp_handle, 2, proto_elem_list); + + /* Make the service browseable */ + SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &list); + + return(sdp_handle); +} + + + #if GATT_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function gatt_set_err_rsp +** +** Description This function is called to set the test confirm value +** +** Returns void +** +*******************************************************************************/ +void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status) +{ + GATT_TRACE_DEBUG3("gatt_set_err_rsp enable=%d op_code=%d, err_status=%d", enable, req_op_code, err_status); + gatt_cb.enable_err_rsp = enable; + gatt_cb.req_op_code = req_op_code; + gatt_cb.err_status = err_status; +} + #endif + + + +/******************************************************************************* +** +** Function gatt_get_regcb +** +** Description The function returns the registration control block. +** +** Returns pointer to the registration control block or NULL +** +*******************************************************************************/ +tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if) +{ + UINT8 ii = (UINT8)gatt_if; + tGATT_REG *p_reg = NULL; + + if (ii) + { + ii--; /* convert from one based to zero based */ + p_reg = &gatt_cb.cl_rcb[ii]; + if ( (ii < GATT_MAX_APPS) && (p_reg->in_use) ) + return(p_reg); + } + + return NULL; +} + + +/******************************************************************************* +** +** Function gatt_is_clcb_allocated +** +** Description The function check clcb for conn_id is allocated or not +** +** Returns True already allocated +** +*******************************************************************************/ + +BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id) +{ + UINT8 i = 0; + BOOLEAN is_allocated= FALSE; + + for (i = 0; i < GATT_CL_MAX_LCB; i++) + { + if (gatt_cb.clcb[i].in_use && (gatt_cb.clcb[i].conn_id == conn_id)) + { + is_allocated = TRUE; + break; + } + } + + return is_allocated; +} + +/******************************************************************************* +** +** Function gatt_clcb_alloc +** +** Description The function allocates a GATT connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id) +{ + UINT8 i = 0; + tGATT_CLCB *p_clcb = NULL; + tGATT_IF gatt_if=GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + for (i = 0; i < GATT_CL_MAX_LCB; i++) + { + if (!gatt_cb.clcb[i].in_use) + { + p_clcb = &gatt_cb.clcb[i]; + + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + p_clcb->clcb_idx = i; + p_clcb->p_reg = p_reg; + p_clcb->p_tcb = p_tcb; + break; + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_clcb_dealloc +** +** Description The function de allocates a GATT connection link control block +** +** Returns None +** +*******************************************************************************/ +void gatt_clcb_dealloc (tGATT_CLCB *p_clcb) +{ + if (p_clcb && p_clcb->in_use) + { + memset(p_clcb, 0, sizeof(tGATT_CLCB)); + } +} + + + +/******************************************************************************* +** +** Function gatt_find_tcb_by_cid +** +** Description The function searches for an empty entry +** in registration info table for GATT client +** +** Returns NULL if not found. Otherwise pointer to the rcb. +** +*******************************************************************************/ +tGATT_TCB * gatt_find_tcb_by_cid (UINT16 lcid) +{ + UINT16 xx = 0; + tGATT_TCB *p_tcb = NULL; + + for (xx = 0; xx < GATT_MAX_PHY_CHANNEL; xx++) + { + if (gatt_cb.tcb[xx].in_use && gatt_cb.tcb[xx].att_lcid == lcid) + { + p_tcb = &gatt_cb.tcb[xx]; + break; + } + } + return p_tcb; +} + + +/******************************************************************************* +** +** Function gatt_num_apps_hold_link +** +** Description The function find the number of applcaitions is holding the link +** +** Returns total number of applications holding this acl link. +** +*******************************************************************************/ +UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb) +{ + UINT8 i, num = 0; + + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_tcb->app_hold_link[i]) + num ++; + } + + GATT_TRACE_DEBUG1("gatt_num_apps_hold_link num=%d", num); + return num; +} + + +/******************************************************************************* +** +** Function gatt_num_clcb_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 i, num = 0; + + for (i = 0; i < GATT_CL_MAX_LCB; i ++) + { + if (gatt_cb.clcb[i].in_use && memcmp(gatt_cb.clcb[i].p_tcb->peer_bda, bda, BD_ADDR_LEN) == 0) + num ++; + } + return num; +} + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ) +{ + UINT8 i; + + if (p_tcb) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_tcb->prep_cnt[i]) + { + p_tcb->sr_cmd.cback_cnt[i]=1; + } + } + } + +} + +/******************************************************************************* +** +** Function gatt_sr_is_cback_cnt_zero +** +** Description The function searches all LCB with macthing bd address +** +** Returns True if thetotal application callback count is zero +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ) +{ + BOOLEAN status = TRUE; + UINT8 i; + + if (p_tcb) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_tcb->sr_cmd.cback_cnt[i]) + { + status = FALSE; + break; + } + } + } + else + { + status = FALSE; + } + return status; +} + +/******************************************************************************* +** +** Function gatt_sr_is_prep_cnt_zero +** +** Description Check the prepare write request count is zero or not +** +** Returns True no prepare write request +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb) +{ + BOOLEAN status = TRUE; + UINT8 i; + + if (p_tcb) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_tcb->prep_cnt[i]) + { + status = FALSE; + break; + } + } + } + else + { + status = FALSE; + } + return status; +} + + +/******************************************************************************* +** +** Function gatt_sr_reset_cback_cnt +** +** Description Reset the application callback count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ) +{ + UINT8 i; + + if (p_tcb) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + p_tcb->sr_cmd.cback_cnt[i]=0; + } + } +} + +/******************************************************************************* +** +** Function gatt_sr_reset_prep_cnt +** +** Description Reset the prep write count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ) +{ + UINT8 i; + if (p_tcb) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + p_tcb->prep_cnt[i]=0; + } + } +} + + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description Update the teh applicaiton callback count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ + + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + if (p_tcb) + { + if (is_reset_first) + { + gatt_sr_reset_cback_cnt(p_tcb); + } + if (is_inc) + { + p_tcb->sr_cmd.cback_cnt[idx]++; + } + else + { + if ( p_tcb->sr_cmd.cback_cnt[idx]) + { + p_tcb->sr_cmd.cback_cnt[idx]--; + } + } + } +} + + +/******************************************************************************* +** +** Function gatt_sr_update_prep_cnt +** +** Description Update the teh prepare write request count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + GATT_TRACE_DEBUG4("gatt_sr_update_prep_cnt tcb idx=%d gatt_if=%d is_inc=%d is_reset_first=%d", + p_tcb->tcb_idx, gatt_if, is_inc, is_reset_first); + + if (p_tcb) + { + if (is_reset_first) + { + gatt_sr_reset_prep_cnt(p_tcb); + } + if (is_inc) + { + p_tcb->prep_cnt[idx]++; + } + else + { + if (p_tcb->prep_cnt[idx]) + { + p_tcb->prep_cnt[idx]--; + } + } + } +} +/******************************************************************************* +** +** Function gatt_cancel_open +** +** Description Cancel open request +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda) +{ + tGATT_TCB *p_tcb=NULL; + BOOLEAN status= TRUE; + + p_tcb = gatt_find_tcb_by_addr(bda); + if (p_tcb) + { + if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) + { + GATT_TRACE_ERROR0("GATT_CancelConnect - link connected Too late to cancel"); + status = FALSE; + } + else + { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) + { + gatt_disconnect(p_tcb->peer_bda); + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatt_find_app_hold_link +** +** Description find the applicaiton that is holding the specified link +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if) +{ + UINT8 i; + BOOLEAN found= FALSE; + + for (i = start_idx; i < GATT_MAX_APPS; i ++) + { + if (p_tcb->app_hold_link[i]) + { + *p_gatt_if = gatt_cb.clcb[i].p_reg->gatt_if; + *p_found_idx = i; + found = TRUE; + break; + } + } + return found; +} + +/******************************************************************************* +** +** Function gatt_cmd_enq +** +** Description Enqueue this command. +** +** Returns None. +** +*******************************************************************************/ +BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->next_slot_inq]; + + p_cmd->to_send = to_send; /* waiting to be sent */ + p_cmd->op_code = op_code; + p_cmd->p_cmd = p_buf; + p_cmd->clcb_idx = clcb_idx; + + if (!to_send) + { + p_tcb->pending_cl_req = p_tcb->next_slot_inq; + } + + p_tcb->next_slot_inq ++; + p_tcb->next_slot_inq %= GATT_CL_MAX_LCB; + + return TRUE; +} + +/******************************************************************************* +** +** Function gatt_cmd_dequeue +** +** Description dequeue the command in the client CCB command queue. +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGATT_CLCB * gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_op_code) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + tGATT_CLCB *p_clcb = NULL; + + if (p_tcb->pending_cl_req != p_tcb->next_slot_inq) + { + p_clcb = &gatt_cb.clcb[p_cmd->clcb_idx]; + + *p_op_code = p_cmd->op_code; + + p_tcb->pending_cl_req ++; + p_tcb->pending_cl_req %= GATT_CL_MAX_LCB; + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_send_write_msg +** +** Description This real function send out the ATT message for write. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, + UINT16 handle, UINT16 len, + UINT16 offset, UINT8 *p_data) +{ + tGATT_CL_MSG msg; + + msg.attr_value.handle = handle; + msg.attr_value.len = len; + msg.attr_value.offset = offset; + + memcpy (msg.attr_value.value, p_data, len); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg); +} + +/******************************************************************************* +** +** Function gatt_act_send_browse +** +** Description This function ends a browse command request, including read +** information request and read by type request. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, + UINT16 e_handle, tBT_UUID uuid) +{ + tGATT_CL_MSG msg; + + msg.browse.s_handle = s_handle; + msg.browse.e_handle = e_handle; + memcpy(&msg.browse.uuid, &uuid, sizeof(tBT_UUID)); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, index, op, &msg); +} + +/******************************************************************************* +** +** Function gatt_end_operation +** +** Description This function ends a discovery, send callback and finalize +** some control value. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data) +{ + tGATT_CL_COMPLETE cb_data; + tGATT_CMPL_CBACK *p_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_cmpl_cb : NULL; + UINT8 op = p_clcb->operation, disc_type=GATT_DISC_MAX; + tGATT_DISC_CMPL_CB *p_disc_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_disc_cmpl_cb : NULL; + UINT16 conn_id; + UINT8 operation; + + GATT_TRACE_DEBUG3 ("gatt_end_operation status=%d op=%d subtype=%d", + status, p_clcb->operation, p_clcb->op_subtype); + + if (p_cmpl_cb != NULL && p_clcb->operation != 0) + { + if (p_clcb->operation == GATTC_OPTYPE_READ) + { + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + cb_data.att_value.handle = p_clcb->s_handle; + cb_data.att_value.len = p_clcb->counter; + if (p_data) + memcpy (cb_data.att_value.value, p_data, cb_data.att_value.len); + } + + if (p_clcb->operation == GATTC_OPTYPE_WRITE) + { + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + cb_data.handle = + cb_data.att_value.handle = p_clcb->s_handle; + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) + { + if (p_data) + { + cb_data.att_value = *((tGATT_VALUE *) p_data); + } + else + { + GATT_TRACE_DEBUG0("Rcv Prepare write rsp but no data"); + } + } + } + + if (p_clcb->operation == GATTC_OPTYPE_CONFIG) + cb_data.mtu = p_clcb->p_tcb->payload_size; + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) + { + disc_type = p_clcb->op_subtype; + } + } + + if (p_clcb->p_attr_buf) + { + GKI_freebuf(p_clcb->p_attr_buf); + } + + operation = p_clcb->operation; + conn_id = p_clcb->conn_id; + + gatt_clcb_dealloc(p_clcb); + + if (p_disc_cmpl_cb && (op == GATTC_OPTYPE_DISCOVERY)) + (*p_disc_cmpl_cb)(conn_id, disc_type, status); + else if (p_cmpl_cb && op) + (*p_cmpl_cb)(conn_id, op, status, &cb_data); + else + GATT_TRACE_WARNING3 ("gatt_end_operation not sent out op=%d p_disc_cmpl_cb:%p p_cmpl_cb:%p", + operation, p_disc_cmpl_cb, p_cmpl_cb); +} + +/******************************************************************************* +** +** Function gatt_cleanup_upon_disc +** +** Description This function cleans up the control blocks when L2CAP channel +** disconnect. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason) +{ + tGATT_TCB *p_tcb = NULL; + tGATT_CLCB *p_clcb; + UINT8 i; + UINT16 conn_id; + tGATT_REG *p_reg=NULL; + + + GATT_TRACE_DEBUG0 ("gatt_cleanup_upon_disc "); + + if ((p_tcb = gatt_find_tcb_by_addr(bda)) != NULL) + { + GATT_TRACE_DEBUG0 ("found p_tcb "); + for (i = 0; i < GATT_CL_MAX_LCB; i ++) + { + p_clcb = &gatt_cb.clcb[i]; + if (p_clcb->in_use && p_clcb->p_tcb == p_tcb) + { + GATT_TRACE_DEBUG2 ("found p_clcb conn_id=%d clcb_idx=%d", p_clcb->conn_id, p_clcb->clcb_idx); + if (p_clcb->operation != GATTC_OPTYPE_NONE) + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + + gatt_clcb_dealloc(p_clcb); + + } + } + + btu_stop_timer (&p_tcb->rsp_timer_ent); + btu_stop_timer (&p_tcb->ind_ack_timer_ent); + btu_stop_timer (&p_tcb->conf_timer_ent); + gatt_free_pending_ind(p_tcb); + + for (i = 0; i < GATT_MAX_APPS; i ++) + { + p_reg = &gatt_cb.cl_rcb[i]; + if (p_reg->in_use && p_reg->app_cb.p_conn_cb) + { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + GATT_TRACE_DEBUG3 ("found p_reg tcb_idx=%d gatt_if=%d conn_id=0x%x", p_tcb->tcb_idx, p_reg->gatt_if, conn_id); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, FALSE, reason); + } + } + memset(p_tcb, 0, sizeof(tGATT_TCB)); + + } + GATT_TRACE_DEBUG0 ("exit gatt_cleanup_upon_disc "); +} +/******************************************************************************* +** +** Function gatt_dbg_req_op_name +** +** Description Get op code description name, for debug information. +** +** Returns UINT8 *: name of the operation. +** +*******************************************************************************/ +UINT8 * gatt_dbg_op_name(UINT8 op_code) +{ + UINT8 pseduo_op_code_idx = op_code & (~GATT_WRITE_CMD_MASK); + + if (op_code == GATT_CMD_WRITE ) + { + pseduo_op_code_idx = 0x14; /* just an index to op_code_name */ + + } + + if (op_code == GATT_SIGN_CMD_WRITE) + { + pseduo_op_code_idx = 0x15; /* just an index to op_code_name */ + } + + if (pseduo_op_code_idx <= GATT_OP_CODE_MAX) + return(UINT8*) op_code_name[pseduo_op_code_idx]; + else + return(UINT8 *)"Op Code Exceed Max"; +} + +/******************************************************************************* +** +** Function gatt_dbg_display_uuid +** +** Description Disaplay the UUID +** +** Returns None +** +*******************************************************************************/ +void gatt_dbg_display_uuid(tBT_UUID bt_uuid) +{ + char str_buf[50]; + int x = 0; + + if (bt_uuid.len == LEN_UUID_16) + { + sprintf(str_buf, "0x%04x", bt_uuid.uu.uuid16); + } + else if (bt_uuid.len == LEN_UUID_128) + { + x += sprintf(&str_buf[x], "0x%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[15], bt_uuid.uu.uuid128[14], + bt_uuid.uu.uuid128[13], bt_uuid.uu.uuid128[12], + bt_uuid.uu.uuid128[11], bt_uuid.uu.uuid128[10], + bt_uuid.uu.uuid128[9], bt_uuid.uu.uuid128[8]); + sprintf(&str_buf[x], "%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[7], bt_uuid.uu.uuid128[6], + bt_uuid.uu.uuid128[5], bt_uuid.uu.uuid128[4], + bt_uuid.uu.uuid128[3], bt_uuid.uu.uuid128[2], + bt_uuid.uu.uuid128[1], bt_uuid.uu.uuid128[0]); + } + else + BCM_STRNCPY_S(str_buf, sizeof(str_buf), "Unknown UUID 0", 15); + + GATT_TRACE_DEBUG1 ("UUID=[%s]", str_buf); + +} + + +/******************************************************************************* +** +** Function gatt_is_bg_dev_for_app +** +** Description find is this one of the background devices for the application +** +** Returns TRUE this is one of the background devices for the application +** +*******************************************************************************/ +BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if) +{ + UINT8 i; + + for (i = 0; i < GATT_MAX_APPS; i ++ ) + { + if (p_dev->in_use && (p_dev->gatt_if[i] == gatt_if)) + { + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function gatt_find_bg_dev +** +** Description find background connection device from the list. +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV * gatt_find_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) + { + if (p_dev_list->in_use && !memcmp(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN)) + { + return p_dev_list; + } + } + return NULL; +} +/******************************************************************************* +** +** Function gatt_alloc_bg_dev +** +** Description allocate a background connection device record +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV * gatt_alloc_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) + { + if (!p_dev_list->in_use) + { + p_dev_list->in_use = TRUE; + memcpy(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN); + + return p_dev_list; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function gatt_add_bg_dev_list +** +** Description add/remove device from the back ground connection device list +** +** Returns pointer to the device record +** +*******************************************************************************/ +BOOLEAN gatt_add_bg_dev_list(tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) + { + p_dev = gatt_alloc_bg_dev(bd_addr); + } + + if (p_dev) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_dev->gatt_if[i] == gatt_if) + { + GATT_TRACE_ERROR0("device already in list"); + return FALSE; + } + else if (p_dev->gatt_if[i] == 0) + { + GATT_TRACE_DEBUG0("add device into list"); + p_dev->gatt_if[i] = gatt_if; + return TRUE; + } + } + } + + GATT_TRACE_ERROR0("no device record available"); + + return FALSE; +} + + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_for_app +** +** Description Remove the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr); + BOOLEAN status; + + if (p_tcb) + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + status = gatt_update_auto_connect_dev(gatt_if, FALSE, bd_addr); + return status; +} + + +/******************************************************************************* +** +** Function gatt_get_num_apps_for_bg_dev +** +** Description Gte the number of applciations for the specified background device +** +** Returns UINT8 total number fo applications +** +*******************************************************************************/ +UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + UINT8 cnt = 0; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) != NULL) + { + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_dev->gatt_if[i]) + cnt++; + } + } + return cnt; +} + +/******************************************************************************* +** +** Function gatt_find_app_for_bg_dev +** +** Description find the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) + { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS; i ++) + { + if (p_dev->gatt_if[i] != 0 ) + { + *p_gatt_if = p_dev->gatt_if[i]; + ret = TRUE; + break; + } + } + return ret; +} + + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_from_list +** +** Description add/remove device from the back ground connection device list +** +** Returns pointer to the device record +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_from_list(tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i, j; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) + { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS && p_dev->gatt_if[i] > 0; i ++) + { + if (p_dev->gatt_if[i] == gatt_if) + { + p_dev->gatt_if[i] = 0; + + for (j = i + 1; j < GATT_MAX_APPS; j ++) + p_dev->gatt_if[j - 1] = p_dev->gatt_if[j]; + + if (p_dev->gatt_if[0] == 0) + { + ret = BTM_BleUpdateBgConnDev(FALSE, p_dev->remote_bda); + memset(p_dev, 0, sizeof(tGATT_BG_CONN_DEV)); + } + else + ret = TRUE; + + break; + } + } + + return ret; +} +/******************************************************************************* +** +** Function gatt_deregister_bgdev_list +** +** Description deregister all related back ground connetion device. +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_deregister_bgdev_list(tGATT_IF gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i , j, k; + + for (i = 0 ; i in_use) + { + for (j = 0; j < GATT_MAX_APPS; j ++) + { + if (p_dev_list->gatt_if[j] == 0) + break; + else if (p_dev_list->gatt_if[j] == gatt_if) + { + for (k = j + 1; k < GATT_MAX_APPS; k ++) + p_dev_list->gatt_if[k - 1] = p_dev_list->gatt_if[k]; + + if (p_dev_list->gatt_if[0] == 0) + { + BTM_BleUpdateBgConnDev(FALSE, p_dev_list->remote_bda); + memset(p_dev_list, 0, sizeof(tGATT_BG_CONN_DEV)); + break; + } + } + } + } + } +} + + +/******************************************************************************* +** +** Function gatt_reset_bgdev_list +** +** Description reset bg device list +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_reset_bgdev_list(void) +{ + memset(&gatt_cb.bgconn_dev, 0 , sizeof(tGATT_BG_CONN_DEV)*GATT_MAX_BG_CONN_DEV); + +} +/******************************************************************************* +** +** Function gatt_update_auto_connect_dev +** +** Description This function add or remove a device for background connection +** procedure. +** +** Parameters gatt_if: Application ID. +** add: add peer device +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr) +{ + BOOLEAN ret = FALSE, exist_dev = FALSE; + tGATT_REG *p_reg; + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr); + + GATT_TRACE_API0 ("gatt_update_auto_connect_dev "); + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) + { + GATT_TRACE_ERROR1("gatt_update_auto_connect_dev - gatt_if is not registered", gatt_if); + return(FALSE); + } + + if (add) + { + /* new device */ + if (gatt_find_bg_dev(bd_addr)) + exist_dev = TRUE; + + if (gatt_add_bg_dev_list(gatt_if, bd_addr)) + { + if (!exist_dev) + { + ret = BTM_BleUpdateBgConnDev(TRUE, bd_addr); + } + else + ret = TRUE; + + /* if a connected device, update the link holding number */ + if (p_tcb != NULL) + gatt_update_app_use_link_flag(gatt_if, p_tcb, TRUE, TRUE); + } + } + else + { + ret = gatt_remove_bg_dev_from_list(gatt_if, bd_addr); + } + return ret; +} + + + +/******************************************************************************* +** +** Function gatt_get_conn_id +** +** Description This function returns a connecttion handle to a ATT server +** if the server is already connected +** +** Parameters gatt_if: client interface. +** bd_addr: peer device address. +** +** Returns Connection handle or invalid handle value +** +*******************************************************************************/ +UINT16 gatt_get_conn_id (tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_REG *p_reg; + tGATT_CLCB *p_clcb; + tGATT_TCB *p_tcb; + UINT8 i; + + GATT_TRACE_API1 ("GATTC_GetConnIfConnected gatt_if=%d", gatt_if); + /* Do we have a transport to the peer ? If not, we are not connected */ + if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) == NULL) + { + GATT_TRACE_EVENT0 ("GATTC_GetConnIfConnected - no TCB found"); + return(GATT_INVALID_CONN_ID); + } + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) + { + GATT_TRACE_ERROR1("GATTC_GetConnIfConnected - gatt_if is not registered", gatt_if); + return(GATT_INVALID_CONN_ID); + } + + /* Now see if the app already has a client control block to that peer */ + for (i = 0, p_clcb = gatt_cb.clcb; i < GATT_CL_MAX_LCB; i++, p_clcb++) + { + if ( p_clcb->in_use && (p_clcb->p_reg == p_reg) && (p_clcb->p_tcb == p_tcb) ) + { + return(p_clcb->conn_id); + } + } + + /* If here, failed to allocate a client control block */ + GATT_TRACE_ERROR1 ("gatt_get_conn_id: not connected- gatt_if: %u", gatt_if); + return(GATT_INVALID_CONN_ID); +} + + + + +#endif + + diff --git a/stack/hcic/hciblecmds.c b/stack/hcic/hciblecmds.c new file mode 100644 index 0000000..417297b --- /dev/null +++ b/stack/hcic/hciblecmds.c @@ -0,0 +1,751 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains function of the HCIC unit to format and send HCI + * commands. + * + ******************************************************************************/ + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "hcidefs.h" +#include "btu.h" + +#include +#include + +#if (defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE) + +BOOLEAN btsnd_hcic_ble_reset(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_RESET); + UINT8_TO_STREAM (pp, 0); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); + +} + +BOOLEAN btsnd_hcic_ble_set_evt_mask (BT_EVENT_MASK event_mask) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_EVENT_MASK)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_EVENT_MASK; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_EVENT_MASK); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_EVENT_MASK); + ARRAY8_TO_STREAM (pp, event_mask); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + + +BOOLEAN btsnd_hcic_ble_read_buffer_size (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_BUFFER_SIZE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_local_spt_feat (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_LOCAL_SPT_FEAT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_local_used_feat (UINT8 feat_set[8]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_USED_FEAT_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_USED_FEAT_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_LOCAL_SPT_FEAT); + ARRAY_TO_STREAM (pp, feat_set, HCIC_PARAM_SIZE_SET_USED_FEAT_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_random_addr (BD_ADDR random_bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_RANDOM_ADDR); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD); + + BDADDR_TO_STREAM (pp, random_bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_write_adv_params (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, UINT8 addr_type_own, + UINT8 addr_type_dir, BD_ADDR direct_bda, + UINT8 channel_map, UINT8 adv_filter_policy) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS ; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS ); + + UINT16_TO_STREAM (pp, adv_int_min); + UINT16_TO_STREAM (pp, adv_int_max); + UINT8_TO_STREAM (pp, adv_type); + UINT8_TO_STREAM (pp, addr_type_own); + UINT8_TO_STREAM (pp, addr_type_dir); + BDADDR_TO_STREAM (pp, direct_bda); + UINT8_TO_STREAM (pp, channel_map); + UINT8_TO_STREAM (pp, adv_filter_policy); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_read_adv_chnl_tx_power (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_ADV_CHNL_TX_POWER); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); + +} + +BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA + 1); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA); + + if (p_data != NULL && data_len > 0) + { + if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA) + data_len = HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA; + + UINT8_TO_STREAM (pp, data_len); + + ARRAY_TO_STREAM (pp, p_data, data_len); + } + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_set_scan_rsp_data (UINT8 data_len, UINT8 *p_scan_rsp) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_RSP_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP + 1); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP); + + if (p_scan_rsp != NULL && data_len > 0) + { + + if (data_len > HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP ) + data_len = HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP; + + UINT8_TO_STREAM (pp, data_len); + + ARRAY_TO_STREAM (pp, p_scan_rsp, data_len); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_adv_enable (UINT8 adv_enable) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_ADV_ENABLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_ADV_ENABLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_ADV_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_ADV_ENABLE); + + UINT8_TO_STREAM (pp, adv_enable); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +BOOLEAN btsnd_hcic_ble_set_scan_params (UINT8 scan_type, + UINT16 scan_int, UINT16 scan_win, + UINT8 addr_type_own, UINT8 scan_filter_policy) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM); + + UINT8_TO_STREAM (pp, scan_type); + UINT16_TO_STREAM (pp, scan_int); + UINT16_TO_STREAM (pp, scan_win); + UINT8_TO_STREAM (pp, addr_type_own); + UINT8_TO_STREAM (pp, scan_filter_policy); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_WRITE_SCAN_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE); + + UINT8_TO_STREAM (pp, scan_enable); + UINT8_TO_STREAM (pp, duplicate); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* link layer connection management commands */ +BOOLEAN btsnd_hcic_ble_create_ll_conn (UINT16 scan_int, UINT16 scan_win, + UINT8 init_filter_policy, + UINT8 addr_type_peer, BD_ADDR bda_peer, + UINT8 addr_type_own, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CREATE_LL_CONN); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN); + + UINT16_TO_STREAM (pp, scan_int); + UINT16_TO_STREAM (pp, scan_win); + UINT8_TO_STREAM (pp, init_filter_policy); + + UINT8_TO_STREAM (pp, addr_type_peer); + BDADDR_TO_STREAM (pp, bda_peer); + UINT8_TO_STREAM (pp, addr_type_own); + + UINT16_TO_STREAM (pp, conn_int_min); + UINT16_TO_STREAM (pp, conn_int_max); + UINT16_TO_STREAM (pp, conn_latency); + UINT16_TO_STREAM (pp, conn_timeout); + + UINT16_TO_STREAM (pp, min_ce_len); + UINT16_TO_STREAM (pp, max_ce_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_create_conn_cancel (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CREATE_CONN_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_white_list_size (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_WHITE_LIST_SIZE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_clear_white_list (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CLEAR_WHITE_LIST)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CLEAR_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_CLEAR_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CLEAR_WHITE_LIST); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_add_white_list (UINT8 addr_type, BD_ADDR bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ADD_WHITE_LIST)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ADD_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_ADD_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ADD_WHITE_LIST); + + UINT8_TO_STREAM (pp, addr_type); + BDADDR_TO_STREAM (pp, bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_remove_from_white_list (UINT8 addr_type, BD_ADDR bda) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REMOVE_WHITE_LIST)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REMOVE_WHITE_LIST; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_REMOVE_WHITE_LIST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REMOVE_WHITE_LIST); + + UINT8_TO_STREAM (pp, addr_type); + BDADDR_TO_STREAM (pp, bda); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_upd_ll_conn_params (UINT16 handle, + UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_UPD_LL_CONN_PARAMS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS); + + UINT16_TO_STREAM (pp, handle); + + UINT16_TO_STREAM (pp, conn_int_min); + UINT16_TO_STREAM (pp, conn_int_max); + UINT16_TO_STREAM (pp, conn_latency); + UINT16_TO_STREAM (pp, conn_timeout); + UINT16_TO_STREAM (pp, min_ce_len); + UINT16_TO_STREAM (pp, max_ce_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_set_host_chnl_class (UINT8 chnl_map[HCIC_BLE_CHNL_MAP_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_SET_HOST_CHNL_CLASS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS); + + ARRAY_TO_STREAM (pp, chnl_map, HCIC_BLE_CHNL_MAP_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_chnl_map (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CHNL_MAP)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CHNL_MAP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_CHNL_MAP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CHNL_MAP); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_remote_feat (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_REMOTE_FEAT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* security management commands */ +BOOLEAN btsnd_hcic_ble_encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + void *p_cmd_cplt_cback) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(sizeof(BT_HDR) + sizeof (void *) + + HCIC_PARAM_SIZE_BLE_ENCRYPT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_ENCRYPT; + p->offset = sizeof(void *); + + *((void **)pp) = p_cmd_cplt_cback; /* Store command complete callback in buffer */ + pp += sizeof(void *); /* Skip over callback pointer */ + + + UINT16_TO_STREAM (pp, HCI_BLE_ENCRYPT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_ENCRYPT); + + memset(pp, 0, HCIC_PARAM_SIZE_BLE_ENCRYPT); + + if (key_len > HCIC_BLE_ENCRYT_KEY_SIZE) key_len = HCIC_BLE_ENCRYT_KEY_SIZE; + if (pt_len > HCIC_BLE_ENCRYT_KEY_SIZE) pt_len = HCIC_BLE_ENCRYT_KEY_SIZE; + + ARRAY_TO_STREAM (pp, key, key_len); + pp += (HCIC_BLE_ENCRYT_KEY_SIZE - key_len); + ARRAY_TO_STREAM (pp, plain_text, pt_len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_rand (void *p_cmd_cplt_cback) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(sizeof(BT_HDR) + sizeof (void *) + + HCIC_PARAM_SIZE_BLE_RAND)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_RAND; + p->offset = sizeof(void *); + + *((void **)pp) = p_cmd_cplt_cback; /* Store command complete callback in buffer */ + pp += sizeof(void *); /* Skip over callback pointer */ + + UINT16_TO_STREAM (pp, HCI_BLE_RAND); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_RAND); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_start_enc (UINT16 handle, UINT8 rand[HCIC_BLE_RAND_DI_SIZE], + UINT16 ediv, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_BLE_START_ENC)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_BLE_START_ENC; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_START_ENC); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_BLE_START_ENC); + + UINT16_TO_STREAM (pp, handle); + ARRAY_TO_STREAM (pp, rand, HCIC_BLE_RAND_DI_SIZE); + UINT16_TO_STREAM (pp, ediv); + ARRAY_TO_STREAM (pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_ltk_req_reply (UINT16 handle, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LTK_REQ_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LTK_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_LTK_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LTK_REQ_REPLY); + + UINT16_TO_STREAM (pp, handle); + ARRAY_TO_STREAM (pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_ltk_req_neg_reply (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_LTK_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_ble_read_supported_states (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_BLE_READ_SUPPORTED_STATES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +#endif diff --git a/stack/hcic/hcicmds.c b/stack/hcic/hcicmds.c new file mode 100644 index 0000000..71a5dc5 --- /dev/null +++ b/stack/hcic/hcicmds.c @@ -0,0 +1,3361 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains function of the HCIC unit to format and send HCI + * commands. + * + ******************************************************************************/ + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "hcidefs.h" +#include "btu.h" + +#include +#include + +#if defined (LMP_TEST) +#include +#define btu_hcif_send_cmd(p1, p2) HCI_CMD_TO_LOWER((p2)) +#else +#include "btm_int.h" /* Included for UIPC_* macro definitions */ +#endif + +BOOLEAN btsnd_hcic_inquiry(const LAP inq_lap, UINT8 duration, UINT8 response_cnt) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_INQUIRY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_INQUIRY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_INQUIRY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_INQUIRY); + + LAP_TO_STREAM (pp, inq_lap); + UINT8_TO_STREAM (pp, duration); + UINT8_TO_STREAM (pp, response_cnt); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_inq_cancel(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_INQ_CANCEL)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_INQ_CANCEL; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_INQUIRY_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_INQ_CANCEL); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_per_inq_mode (UINT16 max_period, UINT16 min_period, + const LAP inq_lap, UINT8 duration, UINT8 response_cnt) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PER_INQ_MODE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PER_INQ_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PERIODIC_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PER_INQ_MODE); + + UINT16_TO_STREAM (pp, max_period); + UINT16_TO_STREAM (pp, min_period); + LAP_TO_STREAM (pp, inq_lap); + UINT8_TO_STREAM (pp, duration); + UINT8_TO_STREAM (pp, response_cnt); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_per_inq (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_EXIT_PER_INQ)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_EXIT_PER_INQ; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_EXIT_PERIODIC_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_EXIT_PER_INQ); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + + +BOOLEAN btsnd_hcic_create_conn(BD_ADDR dest, UINT16 packet_types, + UINT8 page_scan_rep_mode, UINT8 page_scan_mode, + UINT16 clock_offset, UINT8 allow_switch) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CREATE_CONN)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + +#ifndef BT_10A + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN; +#else + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN - 1; +#endif + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CREATE_CONNECTION); +#ifndef BT_10A + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CREATE_CONN); +#else + UINT8_TO_STREAM (pp, (HCIC_PARAM_SIZE_CREATE_CONN - 1)); +#endif + BDADDR_TO_STREAM (pp, dest); + UINT16_TO_STREAM (pp, packet_types); + UINT8_TO_STREAM (pp, page_scan_rep_mode); + UINT8_TO_STREAM (pp, page_scan_mode); + UINT16_TO_STREAM (pp, clock_offset); +#if !defined (BT_10A) + UINT8_TO_STREAM (pp, allow_switch); +#endif +/* If calling from LMP_TEST or ScriptEngine, then send HCI command immediately */ +#if (!defined (LMP_TEST) && !defined(BTISE)) + btm_acl_paging (p, dest); +#else + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +#endif + return (TRUE); +} + +BOOLEAN btsnd_hcic_disconnect (UINT16 handle, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_DISCONNECT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_DISCONNECT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_DISCONNECT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_DISCONNECT); + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +/* If calling from LMP_TEST or ScriptEngine, then send HCI command immediately */ +#if (!defined (LMP_TEST) && !defined(BTISE)) +// btla-specific ++ + // btm_acl_set_discing(TRUE); +// btla-specific -- +#endif + return (TRUE); +} + +#if BTM_SCO_INCLUDED == TRUE +BOOLEAN btsnd_hcic_add_SCO_conn (UINT16 handle, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ADD_SCO_CONN)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ADD_SCO_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ADD_SCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ADD_SCO_CONN); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif /* BTM_SCO_INCLUDED */ + +BOOLEAN btsnd_hcic_create_conn_cancel(BD_ADDR dest) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CREATE_CONN_CANCEL)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CREATE_CONN_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CREATE_CONNECTION_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CREATE_CONN_CANCEL); + + BDADDR_TO_STREAM (pp, dest); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_accept_conn (BD_ADDR dest, UINT8 role) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ACCEPT_CONN)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ACCEPT_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ACCEPT_CONNECTION_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ACCEPT_CONN); + BDADDR_TO_STREAM (pp, dest); + UINT8_TO_STREAM (pp, role); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reject_conn (BD_ADDR dest, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REJECT_CONN)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REJECT_CONN; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REJECT_CONNECTION_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REJECT_CONN); + + BDADDR_TO_STREAM (pp, dest); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_link_key_req_reply (BD_ADDR bd_addr, LINK_KEY link_key) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_LINK_KEY_REQUEST_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + ARRAY16_TO_STREAM (pp, link_key); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_link_key_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_LINK_KEY_REQUEST_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_pin_code_req_reply (BD_ADDR bd_addr, UINT8 pin_code_len, + PIN_CODE pin_code) +{ + BT_HDR *p; + UINT8 *pp; + int i; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PIN_CODE_REQUEST_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, pin_code_len); + + for (i = 0; i < pin_code_len; i++) + *pp++ = *pin_code++; + + for (; i < PIN_CODE_LEN; i++) + *pp++ = 0; + + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_pin_code_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PIN_CODE_REQUEST_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_change_conn_type (UINT16 handle, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CHANGE_CONN_TYPE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CHANGE_CONN_TYPE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CHANGE_CONN_PACKET_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CHANGE_CONN_TYPE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_auth_request (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_AUTHENTICATION_REQUESTED); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_conn_encrypt (UINT16 handle, BOOLEAN enable) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_CONN_ENCRYPT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_CONN_ENCRYPT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_CONN_ENCRYPTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_CONN_ENCRYPT); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, enable); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_change_link_key (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CHANGE_CONN_LINK_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_master_link_key (BOOLEAN key_flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_MASTER_LINK_KEY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_MASTER_LINK_KEY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_MASTER_LINK_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_MASTER_LINK_KEY); + + UINT8_TO_STREAM (pp, key_flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_name_req (BD_ADDR bd_addr, UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, UINT16 clock_offset) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_NAME_REQ)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_NAME_REQ; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RMT_NAME_REQUEST); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_NAME_REQ); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, page_scan_rep_mode); + UINT8_TO_STREAM (pp, page_scan_mode); + UINT16_TO_STREAM (pp, clock_offset); + +/* If calling from LMP_TEST or ScriptEngine, then send HCI command immediately */ +#if (!defined (LMP_TEST) && !defined(BTISE)) + btm_acl_paging (p, bd_addr); +#else + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +#endif + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_name_req_cancel (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RMT_NAME_REQUEST_CANCEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_features_req (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_ext_features (UINT16 handle, UINT8 page_num) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RMT_EXT_FEATURES)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RMT_EXT_FEATURES; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_EXT_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RMT_EXT_FEATURES); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, page_num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rmt_ver_req (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_VERSION_INFO); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_rmt_clk_offset (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RMT_CLOCK_OFFSET); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_lmp_handle (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LMP_HANDLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_setup_esco_conn (UINT16 handle, UINT32 tx_bw, + UINT32 rx_bw, UINT16 max_latency, UINT16 voice, + UINT8 retrans_effort, UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SETUP_ESCO)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SETUP_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SETUP_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SETUP_ESCO); + + UINT16_TO_STREAM (pp, handle); + UINT32_TO_STREAM (pp, tx_bw); + UINT32_TO_STREAM (pp, rx_bw); + UINT16_TO_STREAM (pp, max_latency); + UINT16_TO_STREAM (pp, voice); + UINT8_TO_STREAM (pp, retrans_effort); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_accept_esco_conn (BD_ADDR bd_addr, UINT32 tx_bw, + UINT32 rx_bw, UINT16 max_latency, + UINT16 content_fmt, UINT8 retrans_effort, + UINT16 packet_types) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ACCEPT_ESCO)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ACCEPT_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ACCEPT_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ACCEPT_ESCO); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT32_TO_STREAM (pp, tx_bw); + UINT32_TO_STREAM (pp, rx_bw); + UINT16_TO_STREAM (pp, max_latency); + UINT16_TO_STREAM (pp, content_fmt); + UINT8_TO_STREAM (pp, retrans_effort); + UINT16_TO_STREAM (pp, packet_types); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reject_esco_conn (BD_ADDR bd_addr, UINT8 reason) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REJECT_ESCO)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REJECT_ESCO; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REJECT_ESCO_CONNECTION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REJECT_ESCO); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, reason); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_hold_mode (UINT16 handle, UINT16 max_hold_period, + UINT16 min_hold_period) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_HOLD_MODE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_HOLD_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_HOLD_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_HOLD_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_hold_period); + UINT16_TO_STREAM (pp, min_hold_period); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_sniff_mode (UINT16 handle, UINT16 max_sniff_period, + UINT16 min_sniff_period, UINT16 sniff_attempt, + UINT16 sniff_timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SNIFF_MODE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SNIFF_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SNIFF_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SNIFF_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_sniff_period); + UINT16_TO_STREAM (pp, min_sniff_period); + UINT16_TO_STREAM (pp, sniff_attempt); + UINT16_TO_STREAM (pp, sniff_timeout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_sniff_mode (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_EXIT_SNIFF_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_park_mode (UINT16 handle, UINT16 beacon_max_interval, + UINT16 beacon_min_interval) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_PARK_MODE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_PARK_MODE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_PARK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_PARK_MODE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, beacon_max_interval); + UINT16_TO_STREAM (pp, beacon_min_interval); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_exit_park_mode (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_EXIT_PARK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return TRUE; +} + +BOOLEAN btsnd_hcic_qos_setup (UINT16 handle, UINT8 flags, UINT8 service_type, + UINT32 token_rate, UINT32 peak, UINT32 latency, + UINT32 delay_var) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_QOS_SETUP)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_QOS_SETUP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_QOS_SETUP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_QOS_SETUP); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, flags); + UINT8_TO_STREAM (pp, service_type); + UINT32_TO_STREAM (pp, token_rate); + UINT32_TO_STREAM (pp, peak); + UINT32_TO_STREAM (pp, latency); + UINT32_TO_STREAM (pp, delay_var); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_role_discovery (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ROLE_DISCOVERY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_switch_role (BD_ADDR bd_addr, UINT8 role) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SWITCH_ROLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SWITCH_ROLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SWITCH_ROLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SWITCH_ROLE); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, role); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_policy_set (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_policy_set (UINT16 handle, UINT16 settings) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_POLICY_SET)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_POLICY_SET; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_WRITE_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_POLICY_SET); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, settings); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_def_policy_set (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_DEF_POLICY_SET)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_DEF_POLICY_SET; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_DEF_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_DEF_POLICY_SET); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_def_policy_set (UINT16 settings) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_WRITE_DEF_POLICY_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET); + + UINT16_TO_STREAM (pp, settings); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_flow_specification(UINT16 handle, UINT8 flags, UINT8 flow_direct, + UINT8 service_type, UINT32 token_rate, + UINT32 token_bucket_size, UINT32 peak, + UINT32 latency) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_FLOW_SPEC)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_FLOW_SPEC; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_FLOW_SPECIFICATION); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_FLOW_SPEC); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, flags); + UINT8_TO_STREAM (pp, flow_direct); + UINT8_TO_STREAM (pp, service_type); + UINT32_TO_STREAM (pp, token_rate); + UINT32_TO_STREAM (pp, token_bucket_size); + UINT32_TO_STREAM (pp, peak); + UINT32_TO_STREAM (pp, latency); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_event_mask(UINT8 local_controller_id, BT_EVENT_MASK event_mask) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_EVENT_MASK)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_EVENT_MASK; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_EVENT_MASK); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_EVENT_MASK); + ARRAY8_TO_STREAM (pp, event_mask); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_event_mask_page_2 (UINT8 local_controller_id, BT_EVENT_MASK event_mask) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_EVENT_MASK_PAGE_2)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_EVENT_MASK_PAGE_2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_EVENT_MASK_PAGE_2); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_EVENT_MASK_PAGE_2); + ARRAY8_TO_STREAM (pp, event_mask); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reset (UINT8 local_controller_id) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_RESET)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_RESET; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RESET); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_RESET); + + btu_hcif_send_cmd (local_controller_id, p); +/* If calling from LMP_TEST or ScriptEngine, then send HCI command immediately */ +#if (!defined (LMP_TEST) && !defined(BTISE)) + if (local_controller_id == LOCAL_BR_EDR_CONTROLLER_ID) + { + btm_acl_reset_paging (); + btm_acl_set_discing (FALSE); + } +#endif + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_event_filter (UINT8 filt_type, UINT8 filt_cond_type, + UINT8 *filt_cond, UINT8 filt_cond_len) +{ + BT_HDR *p; + UINT8 *pp; + + /* Use buffer large enough to hold all sizes in this command */ + if ((p = HCI_GET_CMD_BUF(2 + filt_cond_len)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_EVENT_FILTER); + + if (filt_type) + { + p->len = (UINT16)(HCIC_PREAMBLE_SIZE + 2 + filt_cond_len); + UINT8_TO_STREAM (pp, (UINT8)(2 + filt_cond_len)); + + UINT8_TO_STREAM (pp, filt_type); + UINT8_TO_STREAM (pp, filt_cond_type); + + if (filt_cond_type == HCI_FILTER_COND_DEVICE_CLASS) + { + DEVCLASS_TO_STREAM (pp, filt_cond); + filt_cond += DEV_CLASS_LEN; + DEVCLASS_TO_STREAM (pp, filt_cond); + filt_cond += DEV_CLASS_LEN; + + filt_cond_len -= (2 * DEV_CLASS_LEN); + } + else if (filt_cond_type == HCI_FILTER_COND_BD_ADDR) + { + BDADDR_TO_STREAM (pp, filt_cond); + filt_cond += BD_ADDR_LEN; + + filt_cond_len -= BD_ADDR_LEN; + } + + if (filt_cond_len) + ARRAY_TO_STREAM (pp, filt_cond, filt_cond_len); + } + else + { + p->len = (UINT16)(HCIC_PREAMBLE_SIZE + 1); + UINT8_TO_STREAM (pp, 1); + + UINT8_TO_STREAM (pp, filt_type); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_flush (UINT8 local_controller_id, UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_FLUSH); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_pin_type (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PIN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pin_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PIN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_new_unit_key (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_NEW_UNIT_KEY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_NEW_UNIT_KEY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CREATE_NEW_UNIT_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_NEW_UNIT_KEY); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_stored_key (BD_ADDR bd_addr, BOOLEAN read_all_flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_STORED_KEY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_STORED_KEY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_STORED_LINK_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_STORED_KEY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, read_all_flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_stored_key (UINT8 num_keys, BD_ADDR *bd_addr, + LINK_KEY *link_key) +{ + BT_HDR *p; + UINT8 *pp; + int j; + + if ((p = HCI_GET_CMD_BUF(1 + (num_keys * (BD_ADDR_LEN + LINK_KEY_LEN)))) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + + p->len = HCIC_PREAMBLE_SIZE + 1 + (num_keys * (BD_ADDR_LEN + LINK_KEY_LEN)); + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_STORED_LINK_KEY); + UINT8_TO_STREAM (pp, p->len - HCIC_PREAMBLE_SIZE); + + if(num_keys > HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD) + num_keys = HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD; + + UINT8_TO_STREAM (pp, num_keys); + + for (j = 0; j < num_keys; j++) + { + BDADDR_TO_STREAM (pp, bd_addr[j]); + ARRAY16_TO_STREAM (pp, link_key[j]); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_delete_stored_key (BD_ADDR bd_addr, BOOLEAN delete_all_flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_DELETE_STORED_KEY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_DELETE_STORED_KEY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_DELETE_STORED_LINK_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_DELETE_STORED_KEY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, delete_all_flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_change_name (BD_NAME name) +{ + BT_HDR *p; + UINT8 *pp; + UINT16 len = strlen ((char *)name) + 1; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CHANGE_NAME)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CHANGE_NAME; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_CHANGE_LOCAL_NAME); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CHANGE_NAME); + + ARRAY_TO_STREAM (pp, name, len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_name (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_NAME); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_conn_acc_tout (UINT8 local_controller_id) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_CONN_ACCEPT_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_conn_acc_tout (UINT8 local_controller_id, UINT16 timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM2)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_CONN_ACCEPT_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM2); + + UINT16_TO_STREAM (pp, timeout); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_page_tout (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PAGE_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_page_tout (UINT16 timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM2)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGE_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM2); + + UINT16_TO_STREAM (pp, timeout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_scan_enable (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_SCAN_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_scan_enable (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_SCAN_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_pagescan_cfg(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PAGESCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pagescan_cfg(UINT16 interval, UINT16 window) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG); + + UINT16_TO_STREAM (pp, interval); + UINT16_TO_STREAM (pp, window); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_inqscan_cfg(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_INQUIRYSCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inqscan_cfg(UINT16 interval, UINT16 window) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQUIRYSCAN_CFG); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG); + + UINT16_TO_STREAM (pp, interval); + UINT16_TO_STREAM (pp, window); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_auth_enable (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_AUTHENTICATION_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_auth_enable (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_AUTHENTICATION_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_encr_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_ENCRYPTION_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_encr_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_ENCRYPTION_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_dev_class(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_CLASS_OF_DEVICE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_dev_class(DEV_CLASS dev_class) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM3)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM3; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_CLASS_OF_DEVICE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM3); + + DEVCLASS_TO_STREAM (pp, dev_class); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_voice_settings(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_VOICE_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_voice_settings(UINT16 flags) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM2)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM2; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_VOICE_SETTINGS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM2); + + UINT16_TO_STREAM (pp, flags); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_auto_flush_tout (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_AUTO_FLUSH_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_auto_flush_tout (UINT16 handle, UINT16 tout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_AUTO_FLUSH_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, tout); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_num_bcast_xmit (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_NUM_BCAST_REXMITS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_num_bcast_xmit (UINT8 num) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_NUM_BCAST_REXMITS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_hold_mode_act (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_HOLD_MODE_ACTIVITY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_hold_mode_act (UINT8 flags) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_HOLD_MODE_ACTIVITY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flags); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_tx_power (UINT16 handle, UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_TX_POWER)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_TX_POWER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_TRANSMIT_POWER_LEVEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_TX_POWER); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_sco_flow_enable (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_SCO_FLOW_CTRL_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_sco_flow_enable (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_SCO_FLOW_CTRL_ENABLE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_host_flow_ctrl (UINT8 value) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_HC_TO_HOST_FLOW_CTRL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, value); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_host_buf_size (UINT16 acl_len, UINT8 sco_len, + UINT16 acl_num, UINT16 sco_num) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_HOST_BUF_SIZE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_HOST_BUF_SIZE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_HOST_BUFFER_SIZE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_HOST_BUF_SIZE); + + UINT16_TO_STREAM (pp, acl_len); + UINT8_TO_STREAM (pp, sco_len); + UINT16_TO_STREAM (pp, acl_num); + UINT16_TO_STREAM (pp, sco_num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_host_num_xmitted_pkts (UINT8 num_handles, UINT16 *handle, + UINT16 *num_pkts) +{ + BT_HDR *p; + UINT8 *pp; + int j; + + if ((p = HCI_GET_CMD_BUF(1 + (num_handles * 4))) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + 1 + (num_handles * 4); + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_HOST_NUM_PACKETS_DONE); + UINT8_TO_STREAM (pp, p->len - HCIC_PREAMBLE_SIZE); + + UINT8_TO_STREAM (pp, num_handles); + + for (j = 0; j < num_handles; j++) + { + UINT16_TO_STREAM (pp, handle[j]); + UINT16_TO_STREAM (pp, num_pkts[j]); + } + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_link_super_tout (UINT8 local_controller_id, UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LINK_SUPER_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_link_super_tout (UINT8 local_controller_id, UINT16 handle, UINT16 timeout) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_LINK_SUPER_TOUT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, timeout); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_max_iac (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_NUM_SUPPORTED_IAC); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_cur_iac_lap (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_CURRENT_IAC_LAP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_cur_iac_lap (UINT8 num_cur_iac, LAP * const iac_lap) +{ + BT_HDR *p; + UINT8 *pp; + int i; + + if ((p = HCI_GET_CMD_BUF(1 + (LAP_LEN * num_cur_iac))) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + 1 + (LAP_LEN * num_cur_iac); + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_CURRENT_IAC_LAP); + UINT8_TO_STREAM (pp, p->len - HCIC_PREAMBLE_SIZE); + + UINT8_TO_STREAM (pp, num_cur_iac); + + for (i = 0; i < num_cur_iac; i++) + LAP_TO_STREAM (pp, iac_lap[i]); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_page_scan_per (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PAGESCAN_PERIOD_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_page_scan_per (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_PERIOD_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_page_scan_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PAGESCAN_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_page_scan_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/****************************************** +** Lisbon Features +*******************************************/ +#if BTM_SSR_INCLUDED == TRUE + +BOOLEAN btsnd_hcic_sniff_sub_rate(UINT16 handle, UINT16 max_lat, + UINT16 min_remote_lat, UINT16 min_local_lat) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SNIFF_SUB_RATE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SNIFF_SUB_RATE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SNIFF_SUB_RATE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SNIFF_SUB_RATE); + + UINT16_TO_STREAM (pp, handle); + UINT16_TO_STREAM (pp, max_lat); + UINT16_TO_STREAM (pp, min_remote_lat); + UINT16_TO_STREAM (pp, min_local_lat); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif /* BTM_SSR_INCLUDED */ + +#if (BTM_EIR_SERVER_INCLUDED == TRUE) +/**** Extended Inquiry Response Commands ****/ +BOOLEAN btsnd_hcic_read_ext_inquiry_response (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_EXT_INQ_RESPONSE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +void btsnd_hcic_write_ext_inquiry_response (void *buffer, UINT8 fec_req) +{ + BT_HDR *p = (BT_HDR *)buffer; + UINT8 *pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_EXT_INQ_RESP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_EXT_INQ_RESPONSE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_EXT_INQ_RESP); + + UINT8_TO_STREAM (pp, fec_req); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +} +#endif /* BTM_EIR_SERVER_INCLUDED == TRUE */ + +/**** Simple Pairing Commands ****/ +BOOLEAN btsnd_hcic_write_simple_pairing_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_W_SIMP_PAIR)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_W_SIMP_PAIR; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_SIMPLE_PAIRING_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_W_SIMP_PAIR); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_simple_pairing_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_SIMP_PAIR)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_SIMP_PAIR; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_SIMPLE_PAIRING_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_SIMP_PAIR); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_simp_pair_debug_mode(UINT8 debug_mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SIMP_PAIR_DBUG)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SIMP_PAIR_DBUG; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_SIMP_PAIR_DEBUG_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SIMP_PAIR_DBUG); + + UINT8_TO_STREAM (pp, debug_mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_io_cap_req_reply (BD_ADDR bd_addr, UINT8 capability, + UINT8 oob_present, UINT8 auth_req) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_IO_CAP_RESP)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_IO_CAP_RESP; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_IO_CAPABILITY_RESPONSE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_IO_CAP_RESP); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, capability); + UINT8_TO_STREAM (pp, oob_present); + UINT8_TO_STREAM (pp, auth_req); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_io_cap_req_neg_reply (BD_ADDR bd_addr, UINT8 err_code) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_IO_CAP_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, err_code); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_local_oob_data (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_LOCAL_OOB)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_LOCAL_OOB; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_OOB_DATA); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_LOCAL_OOB); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_conf_reply (BD_ADDR bd_addr, BOOLEAN is_yes) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_UCONF_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_UCONF_REPLY; + p->offset = 0; + + if (!is_yes) + { + /* Negative reply */ + UINT16_TO_STREAM (pp, HCI_USER_CONF_VALUE_NEG_REPLY); + } + else + { + /* Confirmation */ + UINT16_TO_STREAM (pp, HCI_USER_CONF_REQUEST_REPLY); + } + + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_UCONF_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_passkey_reply (BD_ADDR bd_addr, UINT32 value) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_U_PKEY_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_U_PKEY_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_USER_PASSKEY_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_U_PKEY_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT32_TO_STREAM (pp, value); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_user_passkey_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_USER_PASSKEY_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rem_oob_reply (BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REM_OOB_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REM_OOB_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REM_OOB_DATA_REQ_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REM_OOB_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + ARRAY16_TO_STREAM (pp, p_c); + ARRAY16_TO_STREAM (pp, p_r); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_rem_oob_neg_reply (BD_ADDR bd_addr) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_REM_OOB_DATA_REQ_NEG_REPLY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY); + + BDADDR_TO_STREAM (pp, bd_addr); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + + +BOOLEAN btsnd_hcic_read_inq_tx_power (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_TX_POWER)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_TX_POWER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_INQ_TX_POWER_LEVEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_TX_POWER); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inq_tx_power (INT8 level) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_W_TX_POWER)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_W_TX_POWER; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQ_TX_POWER_LEVEL); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_W_TX_POWER); + + INT8_TO_STREAM (pp, level); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +#if 0 /* currently not been used */ +BOOLEAN btsnd_hcic_read_default_erroneous_data_rpt (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_R_ERR_DATA_RPT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_R_ERR_DATA_RPT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_ERRONEOUS_DATA_RPT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_R_ERR_DATA_RPT); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif + +BOOLEAN btsnd_hcic_write_default_erroneous_data_rpt (UINT8 flag) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_W_ERR_DATA_RPT)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_W_ERR_DATA_RPT; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_ERRONEOUS_DATA_RPT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_W_ERR_DATA_RPT); + + UINT8_TO_STREAM (pp, flag); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_send_keypress_notif (BD_ADDR bd_addr, UINT8 notif) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SEND_KEYPRESS_NOTIF); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF); + + BDADDR_TO_STREAM (pp, bd_addr); + UINT8_TO_STREAM (pp, notif); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/**** end of Simple Pairing Commands ****/ + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +BOOLEAN btsnd_hcic_enhanced_flush (UINT16 handle, UINT8 packet_type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_ENHANCED_FLUSH)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_ENHANCED_FLUSH; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_ENHANCED_FLUSH); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_ENHANCED_FLUSH); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, packet_type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +#endif + + +BOOLEAN btsnd_hcic_refresh_encryption_key (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + UINT16_TO_STREAM (pp, HCI_REFRESH_ENCRYPTION_KEY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} +/************************* +** End of Lisbon Commands +**************************/ + +BOOLEAN btsnd_hcic_read_local_ver (UINT8 local_controller_id) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_VERSION_INFO); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_local_supported_cmds (UINT8 local_controller_id) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_SUPPORTED_CMDS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_local_features (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_local_ext_features (UINT8 page_num) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_LOCAL_EXT_FEATURES)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_LOCAL_EXT_FEATURES; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOCAL_EXT_FEATURES); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_LOCAL_EXT_FEATURES); + + UINT8_TO_STREAM (pp, page_num); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_buffer_size (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_BUFFER_SIZE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_country_code (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_COUNTRY_CODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_bd_addr (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_BD_ADDR); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_fail_contact_count (UINT8 local_controller_id, UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_FAILED_CONTACT_COUNT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_reset_fail_contact_count (UINT8 local_controller_id, UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_RESET_FAILED_CONTACT_COUNT); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (local_controller_id, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_get_link_quality (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_GET_LINK_QUALITY); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_rssi (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_RSSI); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_loopback_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_LOOPBACK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_loopback_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_LOOPBACK_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_enable_test_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_ENABLE_DEV_UNDER_TEST_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_afh_channel_assessment_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_AFH_ASSESSMENT_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_afh_channel_assessment_mode(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_AFH_ASSESSMENT_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_afh_channels (UINT8 first, UINT8 last) +{ + BT_HDR *p; + UINT8 *pp; + UINT8 channels[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; + int i; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_AFH_CHANNELS)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_AFH_CHANNELS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_AFH_CHANNELS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_AFH_CHANNELS); + + /* Just make sure that caller did not exceed 79 Bluetooth channels */ + if ((first <= last) && (last <= 78)) + { + for (i = first; i <= last; i++) + { + int byte_offset = i / 8; + int bit_offset = i % 8; + channels[byte_offset] &= ~(1 << bit_offset); + } + } + for (i = 0; i < 10; i++) + *pp++ = channels[i]; + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_set_afh_host_channel_class (UINT8 *p_afhchannelmap) +{ + BT_HDR *p; + UINT8 *pp; + int i; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_SET_AFH_CHANNELS)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_SET_AFH_CHANNELS; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_SET_AFH_CHANNELS); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_SET_AFH_CHANNELS); + + /* Copy and convert */ + for (i = 0; i < 10; i++) + *pp++ = p_afhchannelmap[9-i]; + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_afh_channel_map (UINT16 handle) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_CMD_HANDLE)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_CMD_HANDLE; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_AFH_CH_MAP); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_CMD_HANDLE); + + UINT16_TO_STREAM (pp, handle); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_clock (UINT16 handle, UINT8 which_clock) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CLOCK)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CLOCK; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_CLOCK); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CLOCK); + + UINT16_TO_STREAM (pp, handle); + UINT8_TO_STREAM (pp, which_clock); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_inqscan_type(void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_INQSCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inqscan_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQSCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_inquiry_mode (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_inquiry_mode (UINT8 mode) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_INQUIRY_MODE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, mode); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_read_pagescan_type (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_READ_PAGESCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +BOOLEAN btsnd_hcic_write_pagescan_type (UINT8 type) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_WRITE_PARAM1)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_WRITE_PAGESCAN_TYPE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_WRITE_PARAM1); + + UINT8_TO_STREAM (pp, type); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + +/* Must have room to store BT_HDR + max VSC length + callback pointer */ +#if !defined (LMP_TEST) && (HCI_CMD_POOL_BUF_SIZE < 268) +#error "HCI_CMD_POOL_BUF_SIZE must be larger than 268" +#endif + +void btsnd_hcic_vendor_spec_cmd (void *buffer, UINT16 opcode, UINT8 len, + UINT8 *p_data, void *p_cmd_cplt_cback) +{ + BT_HDR *p = (BT_HDR *)buffer; + UINT8 *pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + len; + p->offset = sizeof(void *); + + *((void **)pp) = p_cmd_cplt_cback; /* Store command complete callback in buffer */ + pp += sizeof(void *); /* Skip over callback pointer */ + + UINT16_TO_STREAM (pp, HCI_GRP_VENDOR_SPECIFIC | opcode); + UINT8_TO_STREAM (pp, len); + ARRAY_TO_STREAM (pp, p_data, len); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); +} + +void btsnd_hcic_data (BT_HDR *p_buf, UINT16 len, UINT16 handle, UINT8 boundary, UINT8 broadcast) +{ + UINT8 *p; + + /* Higher layer should have left 4 bytes for us to fill the header */ + p_buf->offset -= 4; + p_buf->len += 4; + + /* Find the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + UINT16_TO_STREAM (p, handle | ((boundary & 3) << 12) | ((broadcast & 3) << 14)); + UINT16_TO_STREAM (p, len); + + HCI_ACL_DATA_TO_LOWER (p_buf); +} + +BOOLEAN btsnd_hcic_nop (void) +{ + BT_HDR *p; + UINT8 *pp; + + if ((p = HCI_GET_CMD_BUF(HCIC_PARAM_SIZE_READ_CMD)) == NULL) + return (FALSE); + + pp = (UINT8 *)(p + 1); + + p->len = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_READ_CMD; + p->offset = 0; + + UINT16_TO_STREAM (pp, HCI_COMMAND_NONE); + UINT8_TO_STREAM (pp, HCIC_PARAM_SIZE_READ_CMD); + + btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p); + return (TRUE); +} + diff --git a/stack/hid/hid_conn.h b/stack/hid/hid_conn.h new file mode 100644 index 0000000..79bb496 --- /dev/null +++ b/stack/hid/hid_conn.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID connection internal definitions + * + ******************************************************************************/ + +#ifndef HID_CONN_H +#define HID_CONN_H + + +/* Define the HID Connection Block +*/ +typedef struct hid_conn +{ +#define HID_CONN_STATE_UNUSED (0) +#define HID_CONN_STATE_CONNECTING_CTRL (1) +#define HID_CONN_STATE_CONNECTING_INTR (2) +#define HID_CONN_STATE_CONFIG (3) +#define HID_CONN_STATE_CONNECTED (4) +#define HID_CONN_STATE_DISCONNECTING (5) +#define HID_CONN_STATE_SECURITY (6) + + UINT8 conn_state; + +#define HID_CONN_FLAGS_IS_ORIG (0x01) +#define HID_CONN_FLAGS_HIS_CTRL_CFG_DONE (0x02) +#define HID_CONN_FLAGS_MY_CTRL_CFG_DONE (0x04) +#define HID_CONN_FLAGS_HIS_INTR_CFG_DONE (0x08) +#define HID_CONN_FLAGS_MY_INTR_CFG_DONE (0x10) +#define HID_CONN_FLAGS_ALL_CONFIGURED (0x1E) /* All the config done */ +#define HID_CONN_FLAGS_CONGESTED (0x20) +#define HID_CONN_FLAGS_INACTIVE (0x40) + + UINT8 conn_flags; + + UINT8 ctrl_id; + UINT16 ctrl_cid; + UINT16 intr_cid; + UINT16 rem_mtu_size; + UINT16 disc_reason; /* Reason for disconnecting (for HID_HDEV_EVT_CLOSE) */ + TIMER_LIST_ENT timer_entry; + +} tHID_CONN; + +#define HID_SEC_CHN 1 +#define HID_NOSEC_CHN 2 + +#define HIDD_SEC_CHN 3 +#define HIDD_NOSEC_CHN 4 + +#endif diff --git a/stack/hid/hidh_api.c b/stack/hid/hidh_api.c new file mode 100644 index 0000000..36ee5ea --- /dev/null +++ b/stack/hid/hidh_api.c @@ -0,0 +1,546 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the HID HOST API entry points + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hiddefs.h" +#include "hidh_api.h" +#include "hidh_int.h" +#include "btm_api.h" +#include "btu.h" + +#if HID_DYNAMIC_MEMORY == FALSE +tHID_HOST_CTB hh_cb; +#endif + +static void hidh_search_callback (UINT16 sdp_result); + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ) +{ + tSDP_UUID uuid_list; + + if( hh_cb.sdp_busy ) + return HID_ERR_SDP_BUSY; + + uuid_list.len = 2; + uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.p_sdp_db = p_db; + SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL); + + if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback)) + { + hh_cb.sdp_cback = sdp_cback ; + hh_cb.sdp_busy = TRUE; + return HID_SUCCESS; + } + else + return HID_ERR_NO_RESOURCES; +} + +void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str ) +{ + tSDP_DISC_ATTR *p_attr; + UINT16 name_len; + + if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL) + { + if((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len ) + { + memcpy( str, (char *) p_attr->attr_value.v.array, name_len ); + str[name_len] = '\0'; + } + else + { + memcpy( str, (char *) p_attr->attr_value.v.array, max_len-1 ); + str[max_len] = '\0'; + } + } + else + str[0] = '\0'; +} + + +static void hidh_search_callback (UINT16 sdp_result) +{ + tSDP_DISCOVERY_DB *p_db = hh_cb.p_sdp_db; + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_subattr1, *p_subattr2, *p_repdesc; + tBT_UUID hid_uuid; + tHID_DEV_SDP_INFO *p_nvi = &hh_cb.sdp_rec; + UINT16 attr_mask = 0; + + hid_uuid.len = LEN_UUID_16; + hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE; + + hh_cb.sdp_busy = FALSE; + + if (sdp_result != SDP_SUCCESS) + { + hh_cb.sdp_cback(sdp_result, 0, NULL); + return; + } + + if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL) + { + hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL); + return; + } + + memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO )); + + /* First, verify the mandatory fields we care about */ + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL) + || (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + || ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL) + || ((p_repdesc = p_subattr2->p_next_attr) == NULL) + || (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE)) + { + hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL); + return; + } + + if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0) + p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value; + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_VIRTUAL_CABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_RECONN_INIT; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) && + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_NORMALLY_CONNECTABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_SDP_DISABLE; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_BATTERY_POWER; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL)&& + (p_attr->attr_value.v.u8) ) + { + attr_mask |= HID_REMOTE_WAKE; + } + + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name ); + hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr ); + hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name ); + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) + { + p_nvi->rel_num = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) + { + p_nvi->ctry_code = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) + { + p_nvi->sub_class = p_attr->attr_value.v.u8; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) + { + p_nvi->hpars_ver = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) + { + attr_mask |= HID_SUP_TOUT_AVLBL; + p_nvi->sup_timeout = p_attr->attr_value.v.u16; + } + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) + { + attr_mask |= HID_SSR_MAX_LATENCY; + p_nvi->ssr_max_latency = p_attr->attr_value.v.u16; + } + else + p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID; + + if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) + { + attr_mask |= HID_SSR_MIN_TOUT; + p_nvi->ssr_min_tout = p_attr->attr_value.v.u16; + } + else + p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID; + + hh_cb.sdp_rec.p_sdp_layer_rec = p_rec; + hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec); +} + + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns void +** +*******************************************************************************/ +void HID_HostInit (void) +{ + memset(&hh_cb, 0, sizeof(tHID_HOST_CTB)); + +#if defined(HID_INITIAL_TRACE_LEVEL) + hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL; +#else + hh_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + +/******************************************************************************* +** +** Function HID_HostSetTraceLevel +** +** Description This function sets the trace level for HID Host. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 HID_HostSetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + hh_cb.trace_level = new_level; + + return (hh_cb.trace_level); +} + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback) +{ + tHID_STATUS st; + + if( hh_cb.reg_flag ) + return HID_ERR_ALREADY_REGISTERED; + + if( dev_cback == NULL ) + return HID_ERR_INVALID_PARAM; + + /* Register with L2CAP */ + if( (st = hidh_conn_reg()) != HID_SUCCESS ) + { + return st; + } + + hh_cb.callback = dev_cback ; + hh_cb.reg_flag = TRUE; + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostDeregister(void) +{ + UINT8 i; + + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + for( i=0; i= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + HID_HostCloseDev( dev_handle ) ; + hh_cb.devices[dev_handle].in_use = FALSE; + hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED; + hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0; + + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle ) +{ + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + if( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN ) + return HID_ERR_ALREADY_CONN; + + hh_cb.devices[dev_handle].conn_tries = 1; + return hidh_conn_initiate( dev_handle ); +} + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** report_id: is only used on GET_REPORT transaction if is specified. +** only valid when it's a non-zero value. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf ) +{ + tHID_STATUS status = HID_SUCCESS; + + if( !hh_cb.reg_flag ) + { + HIDH_TRACE_ERROR0("HID_ERR_NOT_REGISTERED"); + status = HID_ERR_NOT_REGISTERED; + } + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + { + HIDH_TRACE_ERROR0("HID_ERR_INVALID_PARAM"); + status = HID_ERR_INVALID_PARAM; + } + + if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) + { + HIDH_TRACE_ERROR1("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle); + status = HID_ERR_NO_CONNECTION; + } + + if (status != HID_SUCCESS) + { + if (pbuf) + GKI_freebuf ((void *)pbuf); + } + else + status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ; + + return status; +} + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS HID_HostCloseDev( UINT8 dev_handle ) +{ + if( !hh_cb.reg_flag ) + return (HID_ERR_NOT_REGISTERED); + + if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) + return HID_ERR_INVALID_PARAM; + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; + btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ; + + if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) + return HID_ERR_NO_CONNECTION; + + hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; + return hidh_conn_disconnect( dev_handle ); +} + +tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) +{ + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) + { + HIDH_TRACE_ERROR0 ("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDH_TRACE_ERROR0 ("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_INTR, + BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) + { + HIDH_TRACE_ERROR0 ("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + + return( HID_SUCCESS ); +} diff --git a/stack/hid/hidh_conn.c b/stack/hid/hidh_conn.c new file mode 100644 index 0000000..9ed3a0e --- /dev/null +++ b/stack/hid/hidh_conn.c @@ -0,0 +1,1040 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the connection interface functions + * + ******************************************************************************/ + +#include +#include +#include + + +#include "gki.h" +#include "bt_types.h" + +#include "l2cdefs.h" +#include "l2c_api.h" + +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" + +#include "hiddefs.h" + +#include "hidh_api.h" +#include "hidh_int.h" + +static UINT8 find_conn_by_cid (UINT16 cid); +static void hidh_conn_retry (UINT8 dhandle); + +/********************************************************************************/ +/* 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 hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, + UINT16 psm, UINT8 l2cap_id); +static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested); + +static const tL2CAP_APPL_INFO hst_reg_info = +{ + hidh_l2cif_connect_ind, + hidh_l2cif_connect_cfm, + NULL, + hidh_l2cif_config_ind, + hidh_l2cif_config_cfm, + hidh_l2cif_disconnect_ind, + hidh_l2cif_disconnect_cfm, + NULL, + hidh_l2cif_data_ind, + hidh_l2cif_cong_ind, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function hidh_l2cif_reg +** +** Description This function initializes the SDP unit. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_reg (void) +{ + int xx; + + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + hh_cb.l2cap_cfg.mtu_present = TRUE; + hh_cb.l2cap_cfg.mtu = HID_HOST_MTU; + hh_cb.l2cap_cfg.flush_to_present = TRUE; + hh_cb.l2cap_cfg.flush_to = HID_HOST_FLUSH_TO; + + /* Now, register with L2CAP */ + if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info)) + { + HIDH_TRACE_ERROR0 ("HID Control Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info)) + { + L2CA_Deregister( HID_PSM_CONTROL ) ; + HIDH_TRACE_ERROR0 ("HID Interrupt Registration failed"); + return (HID_ERR_L2CAP_FAILED) ; + } + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) + { + hh_cb.devices[xx].in_use = FALSE ; + hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_conn_disconnect +** +** Description This function disconnects a connection. +** +** Returns TRUE if disconnect started, FALSE if already disconnected +** +*******************************************************************************/ +tHID_STATUS hidh_conn_disconnect (UINT8 dhandle) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + + HIDH_TRACE_EVENT0 ("HID - disconnect"); + + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) + { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; + + /* Disconnect both interrupt and control channels */ + if (p_hcon->intr_cid) + L2CA_DisconnectReq (p_hcon->intr_cid); + + if (p_hcon->ctrl_cid) + L2CA_DisconnectReq (p_hcon->ctrl_cid); + } + else + { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + + return (HID_SUCCESS); +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_term +** +** Description HID security check complete callback function. +** +** Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise +** send security block L2C connection response. +** +*******************************************************************************/ +void hidh_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev= (tHID_HOST_DEV_CTB *) p_ref_data; + + if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg); + + } + /* security check fail */ + else if (res != BTM_SUCCESS) + { + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + } +} + +/******************************************************************************* +** +** Function hidh_l2cif_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 hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ + tHID_CONN *p_hcon; + BOOLEAN bAccept = TRUE; + int i; + tHID_HOST_DEV_CTB *p_dev; + + HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + + for( i=0; i < HID_HOST_MAX_DEVICES; i++ ) + { + if( hh_cb.devices[i].in_use && (!memcmp(bd_addr, hh_cb.devices[i].addr, sizeof(BD_ADDR))) ) + break; + } + + if (i >= HID_HOST_MAX_DEVICES) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0); + return; + } + + p_hcon = &hh_cb.devices[i].conn; + p_dev = &hh_cb.devices[i]; + + /* Check we are in the correct state for this */ + if (psm == HID_PSM_INTERRUPT) + { + if (p_hcon->ctrl_cid == 0) + { + HIDH_TRACE_WARNING0 ("HID - Rcvd INTR L2CAP conn ind, but no CTL channel"); + bAccept = FALSE; + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + bAccept = FALSE; + } + } + else /* CTRL channel */ + { +#if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE) + p_hcon->ctrl_cid = p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; +#else + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd CTL L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + bAccept = FALSE; + } +#endif + } + + if (!bAccept) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + + if (psm == HID_PSM_CONTROL) + { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = l2cap_cid; + p_hcon->ctrl_id = l2cap_id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */ + + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if(btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + FALSE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED) + { + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + + return; + } + + /* Transition to the next appropriate state, configuration */ + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + + HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); +} + +/******************************************************************************* +** +** Function hidh_proc_repage_timeout +** +** Description This function handles timeout (to page device). +** +** Returns void +** +*******************************************************************************/ +void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle) +{ + hidh_conn_initiate( (UINT8) p_tle->param ) ; + hh_cb.devices[p_tle->param].conn_tries++; + hh_cb.callback( (UINT8) p_tle->param, HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ; +} + +/******************************************************************************* +** +** Function hidh_sec_check_complete_orig +** +** Description This function checks to see if security procedures are being +** carried out or not.. +** +** Returns void +** +*******************************************************************************/ +void hidh_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data; + UINT8 dhandle; +#if (HID_HOST_MAX_CONN_RETRY > 0) + UINT32 cb_res = HID_ERR_AUTH_FAILED; +#endif + UINT32 reason; + + dhandle = p_dev - &(hh_cb.devices[0]) ; + if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { + HIDH_TRACE_EVENT0 ("HID - Originator security pass."); + p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ + + /* Check if L2CAP started the connection process for interrupt channel */ + if ((p_dev->conn.intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) + { + HIDH_TRACE_WARNING0 ("HID - INTR Originate failed"); + reason = HID_L2CAP_REQ_FAIL ; + hidh_conn_disconnect (dhandle); + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + + if( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) + { +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( res == BTM_DEVICE_TIMEOUT ) + { + if( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY ) + { + hidh_conn_retry (dhandle); + return; + } + else + cb_res = HID_L2CAP_CONN_FAIL | HCI_ERR_PAGE_TIMEOUT ; + } +#endif + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */ + hidh_conn_disconnect(dhandle); + } + +} + +/******************************************************************************* +** +** Function hidh_l2cif_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 hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + tHID_HOST_DEV_CTB *p_dev = NULL; + + /* Find CCB based on CID, and verify we are in a state to accept this message */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + { + p_dev = &hh_cb.devices[dhandle]; + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if ((p_hcon == NULL) + || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) + || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL)) + || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR))) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + return; + } + + if (result != L2CAP_CONN_OK) + { + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + + hidh_conn_disconnect(dhandle); + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) && + (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED || + result == HCI_ERR_PAGE_TIMEOUT) ) + { + hidh_conn_retry(dhandle); + } + else +#endif + { + reason = HID_L2CAP_CONN_FAIL | (UINT32) result ; + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + } + return; + } + /* receive Control Channel connect confirmation */ + if (l2cap_cid == p_hcon->ctrl_cid) + { + /* check security requirement */ + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */ + + btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, + TRUE, BTM_SEC_PROTO_HID, + (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, + &hidh_sec_check_complete_orig, p_dev); + } + else + { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + } + + /* Send a Configuration Request. */ + L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); + + HIDH_TRACE_EVENT1 ("HID - got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); + return; +} + +/******************************************************************************* +** +** Function hidh_l2cif_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + tHID_HOST_DEV_CTB *p_dev; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + { + p_dev = &hh_cb.devices[dhandle]; + p_hcon = &hh_cb.devices[dhandle].conn; + } + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT1 ("HID - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); + + /* Remember the remote MTU size */ + if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU)) + p_hcon->rem_mtu_size = HID_HOST_MTU; + else + p_hcon->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; + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + else + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) + { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT32 reason; + + HIDH_TRACE_EVENT2 ("HID - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* If configuration failed, disconnect the channel(s) */ + if (p_cfg->result != L2CAP_CFG_OK) + { + hidh_conn_disconnect (dhandle); + reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ; + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + return; + } + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + else + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + + /* If all configuration is complete, change state and tell management we are up */ + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) + && (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) + { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + + hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; + hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_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 hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + UINT16 disc_res = HCI_SUCCESS; + UINT16 hid_close_evt_reason; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (ack_needed) + L2CA_DisconnectRsp (l2cap_cid); + + HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) + { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + + if( !ack_needed ) + disc_res = btm_get_acl_disc_reason_code(); + +#if (HID_HOST_MAX_CONN_RETRY > 0) + if( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) && + (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) && + (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE)) + { + hh_cb.devices[dhandle].conn_tries = 0; + hh_cb.devices[dhandle].conn.timer_entry.param = (UINT32) dhandle; + btu_start_timer (&(hh_cb.devices[dhandle].conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); + } + else +#endif + { + /* Set reason code for HID_HDEV_EVT_CLOSE */ + hid_close_evt_reason = p_hcon->disc_reason; + + /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */ + if ((disc_res == HCI_ERR_AUTH_FAILURE) || + (disc_res == HCI_ERR_KEY_MISSING) || + (disc_res == HCI_ERR_HOST_REJECT_SECURITY) || + (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED) || + (disc_res == HCI_ERR_UNIT_KEY_USED) || + (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (disc_res == HCI_ERR_REPEATED_ATTEMPTS)) + { + hid_close_evt_reason = HID_ERR_AUTH_FAILED; + } + + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ; + } + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); + + if (l2cap_cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) + { + hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ; + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_cong_ind +** +** Description This function handles a congestion status event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested) +{ + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + /* Find CCB based on CID */ + if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + return; + } + + HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested); + + if (congested) + p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; + else + { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + + } +} + + +/******************************************************************************* +** +** Function hidh_l2cif_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 ttype, param, rep_type, evt; + UINT8 dhandle; + tHID_CONN *p_hcon = NULL; + + HIDH_TRACE_DEBUG1 ("HID - hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid); + + /* Find CCB based on CID */ + if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES) + p_hcon = &hh_cb.devices[dhandle].conn; + + if (p_hcon == NULL) + { + HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + GKI_freebuf (p_msg); + return; + } + + + ttype = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + rep_type = param & HID_PAR_REP_TYPE_MASK; + p_data++; + + /* Get rid of the data type */ + p_msg->len--; + p_msg->offset++; + + switch (ttype) + { + case HID_TRANS_HANDSHAKE: + hh_cb.callback(dhandle, HID_HDEV_EVT_HANDSHAKE, param, NULL); + GKI_freebuf (p_msg); + break; + + case HID_TRANS_CONTROL: + switch (param) + { + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidh_conn_disconnect( dhandle ) ; + /* Device is unplugging from us. Tell USB */ + hh_cb.callback(dhandle, HID_HDEV_EVT_VC_UNPLUG, 0, NULL); + break; + + default: + break; + } + GKI_freebuf (p_msg); + break; + + + case HID_TRANS_DATA: + evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? + HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA; + hh_cb.callback(dhandle, evt, rep_type, p_msg); + break; + + case HID_TRANS_DATAC: + evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? + HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC; + hh_cb.callback(dhandle, evt, rep_type, p_msg); + break; + + default: + GKI_freebuf (p_msg); + break; + } + +} + +/******************************************************************************* +** +** Function hidh_conn_snd_data +** +** Description This function is sends out data. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param, + UINT16 data, UINT8 report_id, BT_HDR *buf) +{ + tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; + BT_HDR *p_buf; + UINT8 *p_out; + UINT16 bytes_copied; + BOOLEAN seg_req = FALSE; + UINT16 data_size; + UINT16 cid; + UINT8 pool_id; + UINT8 use_data = 0 ; + BOOLEAN blank_datc = FALSE; + + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) + { + if (buf) + GKI_freebuf ((void *)buf); + return( HID_ERR_CONGESTED ); + } + + switch( trans_type ) + { + case HID_TRANS_CONTROL: + case HID_TRANS_GET_REPORT: + case HID_TRANS_SET_REPORT: + case HID_TRANS_GET_PROTOCOL: + case HID_TRANS_SET_PROTOCOL: + case HID_TRANS_GET_IDLE: + case HID_TRANS_SET_IDLE: + cid = p_hcon->ctrl_cid; + pool_id = HID_CONTROL_POOL_ID; + break; + case HID_TRANS_DATA: + cid = p_hcon->intr_cid; + pool_id = HID_INTERRUPT_POOL_ID; + break; + default: + return (HID_ERR_INVALID_PARAM) ; + } + + if( trans_type == HID_TRANS_SET_IDLE ) + use_data = 1; + else if( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) ) + use_data = 2; + + do + { + if ( buf == NULL || blank_datc ) + { + if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) + return (HID_ERR_NO_RESOURCES); + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = FALSE; + data_size = 0; + bytes_copied = 0; + blank_datc = FALSE; + } + else if ( (buf->len > (p_hcon->rem_mtu_size - 1))) + { + if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) + return (HID_ERR_NO_RESOURCES); + + p_buf->offset = L2CAP_MIN_OFFSET; + seg_req = TRUE; + data_size = buf->len; + bytes_copied = p_hcon->rem_mtu_size - 1; + } + else + { + p_buf = buf ; + p_buf->offset -= 1; + seg_req = FALSE; + data_size = buf->len; + bytes_copied = buf->len; + } + + p_out = (UINT8 *)(p_buf + 1) + p_buf->offset; + *p_out++ = HID_BUILD_HDR(trans_type, param); + + /* If report ID required for this device */ + if( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) ) + { + *p_out = report_id; + data_size = bytes_copied = 1; + } + + + if (seg_req) + { + memcpy (p_out, (((UINT8 *)(buf+1)) + buf->offset), bytes_copied); + buf->offset += bytes_copied; + buf->len -= bytes_copied; + } + else if( use_data == 1) + { + *(p_out+bytes_copied) = data & 0xff; + } + else if( use_data == 2 ) + { + *(p_out+bytes_copied) = data & 0xff; + *(p_out+bytes_copied+1) = (data >> 8) & 0xff ; + } + + p_buf->len = bytes_copied + 1 + use_data; + data_size -= bytes_copied; + + /* Send the buffer through L2CAP */ + if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (!L2CA_DataWrite (cid, p_buf))) + return (HID_ERR_CONGESTED); + + if (data_size) + trans_type = HID_TRANS_DATAC; + else if( bytes_copied == (p_hcon->rem_mtu_size - 1) ) + { + trans_type = HID_TRANS_DATAC; + blank_datc = TRUE; + } + + } while ((data_size != 0) || blank_datc ) ; + + return (HID_SUCCESS); +} +/******************************************************************************* +** +** Function hidh_conn_initiate +** +** Description This function is called by the management to create a connection. +** +** Returns void +** +*******************************************************************************/ +tHID_STATUS hidh_conn_initiate (UINT8 dhandle) +{ + UINT8 service_id = BTM_SEC_SERVICE_HID_NOSEC_CTRL; + UINT32 mx_chan_id = HID_NOSEC_CHN; + + tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; + + if( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED ) + return( HID_ERR_CONN_IN_PROCESS ); + + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */ + + /* We are the originator of this connection */ + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + + if(p_dev->attr_mask & HID_SEC_REQUIRED) + { + service_id = BTM_SEC_SERVICE_HID_SEC_CTRL; + mx_chan_id = HID_SEC_CHN; + } + BTM_SetOutService (p_dev->addr, service_id, mx_chan_id); + + /* Check if L2CAP started the connection process */ + if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0) + { + HIDH_TRACE_WARNING0 ("HID - Originate failed"); + dhandle = (p_dev - &(hh_cb.devices[0]))/(sizeof( tHID_HOST_DEV_CTB )) ; + hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL ) ; + } + else + { + /* Transition to the next appropriate state, waiting for connection confirm on control channel. */ + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + + return( HID_SUCCESS ); +} + + +/******************************************************************************* +** +** Function find_conn_by_cid +** +** Description This function finds a connection control block based on CID +** +** Returns address of control block, or NULL if not found +** +*******************************************************************************/ +static UINT8 find_conn_by_cid (UINT16 cid) +{ + UINT8 xx; + + for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) + { + if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED) + && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid))) + break; + } + + return (xx); +} + +void hidh_conn_dereg( void ) +{ + L2CA_Deregister (HID_PSM_CONTROL); + L2CA_Deregister (HID_PSM_INTERRUPT); +} + +/******************************************************************************* +** +** Function hidh_conn_retry +** +** Description This function is called to retry a failed connection. +** +** Returns void +** +*******************************************************************************/ +static void hidh_conn_retry( UINT8 dhandle ) +{ + tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; + + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + p_dev->conn.timer_entry.param = (UINT32) dhandle; +#if (HID_HOST_REPAGE_WIN > 0) + btu_start_timer (&(p_dev->conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN); +#else + hidh_proc_repage_timeout( &(p_dev->conn.timer_entry) ); +#endif +} diff --git a/stack/hid/hidh_int.h b/stack/hid/hidh_int.h new file mode 100644 index 0000000..ad07412 --- /dev/null +++ b/stack/hid/hidh_int.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID HOST internal definitions + * + ******************************************************************************/ + +#ifndef HIDH_INT_H +#define HIDH_INT_H + +#include "hidh_api.h" +#include "hid_conn.h" +#include "l2c_api.h" + +enum { + HID_DEV_NO_CONN, + HID_DEV_CONNECTED +}; + +typedef struct per_device_ctb +{ + BOOLEAN in_use; + BD_ADDR addr; /* BD-Addr of the host device */ + UINT16 attr_mask; /* 0x01- virtual_cable; 0x02- normally_connectable; 0x03- reconn_initiate; + 0x04- sdp_disable; */ + UINT8 state; /* Device state if in HOST-KNOWN mode */ + UINT8 conn_substate; + UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */ + + tHID_CONN conn; /* L2CAP channel info */ +} tHID_HOST_DEV_CTB; + +typedef struct host_ctb +{ + tHID_HOST_DEV_CTB devices[HID_HOST_MAX_DEVICES]; + tHID_HOST_DEV_CALLBACK *callback; /* Application callbacks */ + tL2CAP_CFG_INFO l2cap_cfg; + +#define MAX_SERVICE_DB_SIZE 4000 + + BOOLEAN sdp_busy; + tHID_HOST_SDP_CALLBACK *sdp_cback; + tSDP_DISCOVERY_DB *p_sdp_db; + tHID_DEV_SDP_INFO sdp_rec; + BOOLEAN reg_flag; + UINT8 trace_level; +} tHID_HOST_CTB; + +extern tHID_STATUS hidh_conn_snd_data(UINT8 dhandle, UINT8 trans_type, UINT8 param, \ + UINT16 data,UINT8 rpt_id, BT_HDR *buf); +extern tHID_STATUS hidh_conn_reg (void); +extern void hidh_conn_dereg( void ); +extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle); +extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle); +extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle); + +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if HID_DYNAMIC_MEMORY == FALSE +HID_API extern tHID_HOST_CTB hh_cb; +#else +HID_API extern tHID_HOST_CTB *hidh_cb_ptr; +#define hh_cb (*hidh_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/include/a2d_api.h b/stack/include/a2d_api.h new file mode 100644 index 0000000..f21ba6c --- /dev/null +++ b/stack/include/a2d_api.h @@ -0,0 +1,257 @@ +/****************************************************************************** + * + * Copyright (C) 2000-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to A2DP Application Programming Interface + * + ******************************************************************************/ +#ifndef A2D_API_H +#define A2D_API_H +#include "sdp_api.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* Profile supported features */ +#define A2D_SUPF_PLAYER 0x0001 +#define A2D_SUPF_MIC 0x0002 +#define A2D_SUPF_TUNER 0x0004 +#define A2D_SUPF_MIXER 0x0008 + +#define A2D_SUPF_HEADPHONE 0x0001 +#define A2D_SUPF_SPEAKER 0x0002 +#define A2D_SUPF_RECORDER 0x0004 +#define A2D_SUPF_AMP 0x0008 + +/* AV Media Types */ +#define A2D_MEDIA_TYPE_AUDIO 0x00 /* audio media type + RFA */ +#define A2D_MEDIA_TYPE_VIDEO 0x10 /* video media type + RFA */ +#define A2D_MEDIA_TYPE_MULTI 0x20 /* multimedia media type + RFA */ + +/* AV Media Codec Type (Audio Codec ID) */ +#define A2D_MEDIA_CT_SBC 0x00 /* SBC media codec type */ +#define A2D_MEDIA_CT_M12 0x01 /* MPEG-1, 2 Audio media codec type */ +#define A2D_MEDIA_CT_M24 0x02 /* MPEG-2, 4 AAC media codec type */ +#define A2D_MEDIA_CT_ATRAC 0x04 /* ATRAC family media codec type */ + +#define A2D_SUCCESS 0 /* Success */ +#define A2D_FAIL 0x0A /* Failed */ +#define A2D_BUSY 0x0B /* A2D_FindService is already in progress */ +#define A2D_INVALID_PARAMS 0x0C /* bad parameters */ +#define A2D_WRONG_CODEC 0x0D /* wrong codec info */ +#define A2D_BAD_CODEC_TYPE 0xC1 /* Media Codec Type is not valid */ +#define A2D_NS_CODEC_TYPE 0xC2 /* Media Codec Type is not supported */ +#define A2D_BAD_SAMP_FREQ 0xC3 /* Sampling Frequency is not valid or multiple values have been selected */ +#define A2D_NS_SAMP_FREQ 0xC4 /* Sampling Frequency is not supported */ +#define A2D_BAD_CH_MODE 0xC5 /* Channel Mode is not valid or multiple values have been selected */ +#define A2D_NS_CH_MODE 0xC6 /* Channel Mode is not supported */ +#define A2D_BAD_SUBBANDS 0xC7 /* None or multiple values have been selected for Number of Subbands */ +#define A2D_NS_SUBBANDS 0xC8 /* Number of Subbands is not supported */ +#define A2D_BAD_ALLOC_MTHD 0xC9 /* None or multiple values have been selected for Allocation Method */ +#define A2D_NS_ALLOC_MTHD 0xCA /* Allocation Method is not supported */ +#define A2D_BAD_MIN_BITPOOL 0xCB /* Minimum Bitpool Value is not valid */ +#define A2D_NS_MIN_BITPOOL 0xCC /* Minimum Bitpool Value is not supported */ +#define A2D_BAD_MAX_BITPOOL 0xCD /* Maximum Bitpool Value is not valid */ +#define A2D_NS_MAX_BITPOOL 0xCE /* Maximum Bitpool Value is not supported */ +#define A2D_BAD_LAYER 0xCF /* None or multiple values have been selected for Layer */ +#define A2D_NS_LAYER 0xD0 /* Layer is not supported */ +#define A2D_NS_CRC 0xD1 /* CRC is not supported */ +#define A2D_NS_MPF 0xD2 /* MPF-2 is not supported */ +#define A2D_NS_VBR 0xD3 /* VBR is not supported */ +#define A2D_BAD_BIT_RATE 0xD4 /* None or multiple values have been selected for Bit Rate */ +#define A2D_NS_BIT_RATE 0xD5 /* Bit Rate is not supported */ +#define A2D_BAD_OBJ_TYPE 0xD6 /* Either 1) Object type is not valid (b3-b0) or 2) None or multiple values have been selected for Object Type */ +#define A2D_NS_OBJ_TYPE 0xD7 /* Object type is not supported */ +#define A2D_BAD_CHANNEL 0xD8 /* None or multiple values have been selected for Channels */ +#define A2D_NS_CHANNEL 0xD9 /* Channels is not supported */ +#define A2D_BAD_BLOCK_LEN 0xDD /* None or multiple values have been selected for Block Length */ +#define A2D_BAD_CP_TYPE 0xE0 /* The requested CP Type is not supported. */ +#define A2D_BAD_CP_FORMAT 0xE1 /* The format of Content Protection Service Capability/Content Protection Scheme Dependent Data is not correct. */ + +typedef UINT8 tA2D_STATUS; + +/* the return values from A2D_BitsSet() */ +#define A2D_SET_ONE_BIT 1 /* one and only one bit is set */ +#define A2D_SET_ZERO_BIT 0 /* all bits clear */ +#define A2D_SET_MULTL_BIT 2 /* multiple bits are set */ + +/***************************************************************************** +** type definitions +*****************************************************************************/ + +/* This data type is used in A2D_FindService() to initialize the SDP database + * to hold the result service search. */ +typedef struct +{ + UINT32 db_len; /* Length, in bytes, of the discovery database */ + UINT16 num_attr;/* The number of attributes in p_attrs */ + tSDP_DISCOVERY_DB *p_db; /* Pointer to the discovery database */ + UINT16 *p_attrs; /* The attributes filter. If NULL, A2DP API sets the attribute filter + * to be ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_BT_PROFILE_DESC_LIST, + * ATTR_ID_SUPPORTED_FEATURES, ATTR_ID_SERVICE_NAME and ATTR_ID_PROVIDER_NAME. + * If not NULL, the input is taken as the filter. */ +} tA2D_SDP_DB_PARAMS; + +/* This data type is used in tA2D_FIND_CBACK to report the result of the SDP discovery process. */ +typedef struct +{ + UINT16 service_len; /* Length, in bytes, of the service name */ + UINT16 provider_len; /* Length, in bytes, of the provider name */ + char * p_service_name; /* Pointer the service name. This character string may not be null terminated. + * Use the service_len parameter to safely copy this string */ + char * p_provider_name;/* Pointer the provider name. This character string may not be null terminated. + * Use the provider_len parameter to safely copy this string */ + UINT16 features; /* Profile supported features */ + UINT16 avdt_version; /* AVDTP protocol version */ +} tA2D_Service; + +/* This is the callback to notify the result of the SDP discovery process. */ +typedef void (tA2D_FIND_CBACK)(BOOLEAN found, tA2D_Service * p_service); + + +/***************************************************************************** +** external function declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/****************************************************************************** +** +** Function A2D_AddRecord +** +** Description This function is called by a server application to add +** SRC or SNK information to an SDP record. Prior to +** calling this function the application must call +** SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** +** features: Profile supported features. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +A2D_API extern tA2D_STATUS A2D_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provider_name, + UINT16 features, UINT32 sdp_handle); + +/****************************************************************************** +** +** Function A2D_FindService +** +** Description This function is called by a client application to +** perform service discovery and retrieve SRC or SNK SDP +** record information from a server. Information is +** returned for the first service record found on the +** server that matches the service UUID. The callback +** function will be executed when service discovery is +** complete. There can only be one outstanding call to +** A2D_FindService() at a time; the application must wait +** for the callback before it makes another call to +** the function. +** +** Input Parameters: +** service_uuid: Indicates SRC or SNK. +** +** bd_addr: BD address of the peer device. +** +** p_db: Pointer to the information to initialize +** the discovery database. +** +** p_cback: Pointer to the A2D_FindService() +** callback function. +** +** Output Parameters: +** None. +** +** Returns A2D_SUCCESS if function execution succeeded, +** A2D_INVALID_PARAMS if bad parameters are given. +** A2D_BUSY if discovery is already in progress. +** A2D_FAIL if function execution failed. +** +******************************************************************************/ +A2D_API extern tA2D_STATUS A2D_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tA2D_SDP_DB_PARAMS *p_db, tA2D_FIND_CBACK *p_cback); + +/****************************************************************************** +** +** Function A2D_SetTraceLevel +** +** Description Sets the trace level for A2D. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the A2D tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +A2D_API extern UINT8 A2D_SetTraceLevel (UINT8 new_level); + +/****************************************************************************** +** Function A2D_BitsSet +** +** Description Check the given num for the number of bits set +** Returns A2D_SET_ONE_BIT, if one and only one bit is set +** A2D_SET_ZERO_BIT, if all bits clear +** A2D_SET_MULTL_BIT, if multiple bits are set +******************************************************************************/ +A2D_API extern UINT8 A2D_BitsSet(UINT8 num); + +#ifdef __cplusplus +} +#endif + +/******************************************************************************* +** +** Function A2D_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +A2D_API extern void A2D_Init(void); + +#endif /* A2D_API_H */ diff --git a/stack/include/a2d_sbc.h b/stack/include/a2d_sbc.h new file mode 100644 index 0000000..dad0b8b --- /dev/null +++ b/stack/include/a2d_sbc.h @@ -0,0 +1,212 @@ +/****************************************************************************** + * + * Copyright (C) 2000-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to low complexity subband codec (SBC) + * + ******************************************************************************/ +#ifndef A2D_SBC_H +#define A2D_SBC_H + +/***************************************************************************** +** Constants +*****************************************************************************/ +/* the length of the SBC Media Payload header. */ +#define A2D_SBC_MPL_HDR_LEN 1 + +/* the LOSC of SBC media codec capabilitiy */ +#define A2D_SBC_INFO_LEN 6 + +/* for Codec Specific Information Element */ +#define A2D_SBC_IE_SAMP_FREQ_MSK 0xF0 /* b7-b4 sampling frequency */ +#define A2D_SBC_IE_SAMP_FREQ_16 0x80 /* b7:16 kHz */ +#define A2D_SBC_IE_SAMP_FREQ_32 0x40 /* b6:32 kHz */ +#define A2D_SBC_IE_SAMP_FREQ_44 0x20 /* b5:44.1kHz */ +#define A2D_SBC_IE_SAMP_FREQ_48 0x10 /* b4:48 kHz */ + +#define A2D_SBC_IE_CH_MD_MSK 0x0F /* b3-b0 channel mode */ +#define A2D_SBC_IE_CH_MD_MONO 0x08 /* b3: mono */ +#define A2D_SBC_IE_CH_MD_DUAL 0x04 /* b2: dual */ +#define A2D_SBC_IE_CH_MD_STEREO 0x02 /* b1: stereo */ +#define A2D_SBC_IE_CH_MD_JOINT 0x01 /* b0: joint stereo */ + +#define A2D_SBC_IE_BLOCKS_MSK 0xF0 /* b7-b4 number of blocks */ +#define A2D_SBC_IE_BLOCKS_4 0x80 /* 4 blocks */ +#define A2D_SBC_IE_BLOCKS_8 0x40 /* 8 blocks */ +#define A2D_SBC_IE_BLOCKS_12 0x20 /* 12blocks */ +#define A2D_SBC_IE_BLOCKS_16 0x10 /* 16blocks */ + +#define A2D_SBC_IE_SUBBAND_MSK 0x0C /* b3-b2 number of subbands */ +#define A2D_SBC_IE_SUBBAND_4 0x08 /* b3: 4 */ +#define A2D_SBC_IE_SUBBAND_8 0x04 /* b2: 8 */ + +#define A2D_SBC_IE_ALLOC_MD_MSK 0x03 /* b1-b0 allocation mode */ +#define A2D_SBC_IE_ALLOC_MD_S 0x02 /* b1: SNR */ +#define A2D_SBC_IE_ALLOC_MD_L 0x01 /* b0: loundess */ + +#define A2D_SBC_IE_MIN_BITPOOL 2 +#define A2D_SBC_IE_MAX_BITPOOL 250 + +/* for media payload header */ +#define A2D_SBC_HDR_F_MSK 0x80 +#define A2D_SBC_HDR_S_MSK 0x40 +#define A2D_SBC_HDR_L_MSK 0x20 +#define A2D_SBC_HDR_NUM_MSK 0x0F + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* data type for the SBC Codec Information Element*/ +typedef struct +{ + UINT8 samp_freq; /* Sampling frequency */ + UINT8 ch_mode; /* Channel mode */ + UINT8 block_len; /* Block length */ + UINT8 num_subbands; /* Number of subbands */ + UINT8 alloc_mthd; /* Allocation method */ + UINT8 max_bitpool; /* Maximum bitpool */ + UINT8 min_bitpool; /* Minimum bitpool */ +} tA2D_SBC_CIE; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/****************************************************************************** +** +** Function A2D_SbcChkFrInit +** +** Description check if need to init the descramble control block. +** +** Returns nothing. +******************************************************************************/ +A2D_API extern void A2D_SbcChkFrInit(UINT8 *p_pkt); + +/****************************************************************************** +** +** Function A2D_SbcDescramble +** +** Description descramble the packet. +** +** Returns nothing. +******************************************************************************/ +A2D_API extern void A2D_SbcDescramble(UINT8 *p_pkt, UINT16 len); + +/****************************************************************************** +** +** Function A2D_BldSbcInfo +** +** Description This function is called by an application to build +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** media_type: Indicates Audio, or Multimedia. +** +** p_ie: The SBC Codec Information Element information. +** +** Output Parameters: +** p_result: the resulting codec info byte sequence. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +A2D_API extern tA2D_STATUS A2D_BldSbcInfo(UINT8 media_type, tA2D_SBC_CIE *p_ie, + UINT8 *p_result); + +/****************************************************************************** +** +** Function A2D_ParsSbcInfo +** +** Description This function is called by an application to parse +** the SBC Media Codec Capabilities byte sequence +** beginning from the LOSC octet. +** Input Parameters: +** p_info: the byte sequence to parse. +** +** for_caps: TRUE, if the byte sequence is for get capabilities response. +** +** Output Parameters: +** p_ie: The SBC Codec Information Element information. +** +** Returns A2D_SUCCESS if function execution succeeded. +** Error status code, otherwise. +******************************************************************************/ +A2D_API extern tA2D_STATUS A2D_ParsSbcInfo(tA2D_SBC_CIE *p_ie, UINT8 *p_info, + BOOLEAN for_caps); + +/****************************************************************************** +** +** Function A2D_BldSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Output Parameters: +** p_dst: the resulting media payload header byte sequence. +** +** Returns void. +******************************************************************************/ +A2D_API extern void A2D_BldSbcMplHdr(UINT8 *p_dst, BOOLEAN frag, BOOLEAN start, + BOOLEAN last, UINT8 num); + +/****************************************************************************** +** +** Function A2D_ParsSbcMplHdr +** +** Description This function is called by an application to parse +** the SBC Media Payload header. +** Input Parameters: +** p_src: the byte sequence to parse.. +** +** Output Parameters: +** frag: 1, if fragmented. 0, otherwise. +** +** start: 1, if the starting packet of a fragmented frame. +** +** last: 1, if the last packet of a fragmented frame. +** +** num: If frag is 1, this is the number of remaining fragments +** (including this fragment) of this frame. +** If frag is 0, this is the number of frames in this packet. +** +** Returns void. +******************************************************************************/ +A2D_API extern void A2D_ParsSbcMplHdr(UINT8 *p_src, BOOLEAN *p_frag, + BOOLEAN *p_start, BOOLEAN *p_last, + UINT8 *p_num); +#ifdef __cplusplus +} +#endif + +#endif /* A2D_SBC_H */ diff --git a/stack/include/avct_api.h b/stack/include/avct_api.h new file mode 100644 index 0000000..2880011 --- /dev/null +++ b/stack/include/avct_api.h @@ -0,0 +1,279 @@ +/****************************************************************************** + * + * 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 interface file contains the interface to the Audio Video Control + * Transport Protocol (AVCTP). + * + ******************************************************************************/ +#ifndef AVCT_API_H +#define AVCT_API_H + +#include "bt_types.h" +#include "bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* API function return value result codes. */ +#define AVCT_SUCCESS 0 /* Function successful */ +#define AVCT_NO_RESOURCES 1 /* Not enough resources */ +#define AVCT_BAD_HANDLE 2 /* Bad handle */ +#define AVCT_PID_IN_USE 3 /* PID already in use */ +#define AVCT_NOT_OPEN 4 /* Connection not open */ + +/* PSM for AVCT. */ +#define AVCT_PSM 0x0017 +#define AVCT_BR_PSM 0x001B + +/* Protocol revision numbers */ +#define AVCT_REV_1_0 0x0100 +#define AVCT_REV_1_2 0x0102 +#define AVCT_REV_1_3 0x0103 + +/* the layer_specific settings */ +#define AVCT_DATA_CTRL 0x0001 /* for the control channel */ +#define AVCT_DATA_BROWSE 0x0002 /* for the browsing channel */ +#define AVCT_DATA_PARTIAL 0x0100 /* Only have room for a partial message */ + +#define AVCT_MIN_CONTROL_MTU 48 /* Per the AVRC spec, minimum MTU for the control channel */ +#define AVCT_MIN_BROWSE_MTU 335 /* Per the AVRC spec, minimum MTU for the browsing channel */ + +/* Message offset. The number of bytes needed by the protocol stack for the +** protocol headers of an AVCTP message packet. +*/ +#define AVCT_MSG_OFFSET 15 +#define AVCT_BROWSE_OFFSET 17 /* the default offset for browsing channel */ + +/* Connection role. */ +#define AVCT_INT 0 /* Initiator connection */ +#define AVCT_ACP 1 /* Acceptor connection */ + +/* Control role. */ +#define AVCT_TARGET 1 /* target */ +#define AVCT_CONTROL 2 /* controller */ +#define AVCT_PASSIVE 4 /* If conflict, allow the other side to succeed */ + +/* Command/Response indicator. */ +#define AVCT_CMD 0 /* Command message */ +#define AVCT_RSP 2 /* Response message */ +#define AVCT_REJ 3 /* Message rejected */ + +/* Control callback events. */ +#define AVCT_CONNECT_CFM_EVT 0 /* Connection confirm */ +#define AVCT_CONNECT_IND_EVT 1 /* Connection indication */ +#define AVCT_DISCONNECT_CFM_EVT 2 /* Disconnect confirm */ +#define AVCT_DISCONNECT_IND_EVT 3 /* Disconnect indication */ +#define AVCT_CONG_IND_EVT 4 /* Congestion indication */ +#define AVCT_UNCONG_IND_EVT 5 /* Uncongestion indication */ +#define AVCT_BROWSE_CONN_CFM_EVT 6 /* Browse Connection confirm */ +#define AVCT_BROWSE_CONN_IND_EVT 7 /* Browse Connection indication */ +#define AVCT_BROWSE_DISCONN_CFM_EVT 8 /* Browse Disconnect confirm */ +#define AVCT_BROWSE_DISCONN_IND_EVT 9 /* Browse Disconnect indication */ +#define AVCT_BROWSE_CONG_IND_EVT 10 /* Congestion indication */ +#define AVCT_BROWSE_UNCONG_IND_EVT 11 /* Uncongestion indication */ + + +/* General purpose failure result code for callback events. */ +#define AVCT_RESULT_FAIL 5 + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Control callback function. */ +typedef void (tAVCT_CTRL_CBACK)(UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr); + +/* Message callback function */ +/* p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE */ +typedef void (tAVCT_MSG_CBACK)(UINT8 handle, UINT8 label, UINT8 cr, + BT_HDR *p_pkt); + +/* Structure used by AVCT_CreateConn. */ +typedef struct { + tAVCT_CTRL_CBACK *p_ctrl_cback; /* Control callback */ + tAVCT_MSG_CBACK *p_msg_cback; /* Message callback */ + UINT16 pid; /* Profile ID */ + UINT8 role; /* Initiator/acceptor role */ + UINT8 control; /* Control role (Control/Target) */ +} tAVCT_CC; + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVCT_Register +** +** Description This is the system level registration function for the +** AVCTP protocol. This function initializes AVCTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVCTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +AVCT_API extern void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask); + +/******************************************************************************* +** +** Function AVCT_Deregister +** +** Description This function is called to deregister use AVCTP protocol. +** It is called when AVCTP is no longer being used by any +** application in the system. Before this function can be +** called, all connections must be removed with +** AVCT_RemoveConn(). +** +** +** Returns void +** +*******************************************************************************/ +AVCT_API extern void AVCT_Deregister(void); + +/******************************************************************************* +** +** Function AVCT_CreateConn +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, + BD_ADDR peer_addr); + +/******************************************************************************* +** +** Function AVCT_RemoveConn +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_RemoveConn(UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_CreateBrowse +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_CreateBrowse(UINT8 handle, UINT8 role); + +/******************************************************************************* +** +** Function AVCT_RemoveBrowse +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_RemoveBrowse(UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_GetBrowseMtu +** +** Description Get the peer_mtu for the AVCTP Browse channel of the given +** connection. +** +** Returns the peer browsing channel MTU. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_GetBrowseMtu (UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_GetPeerMtu +** +** Description Get the peer_mtu for the AVCTP channel of the given +** connection. +** +** Returns the peer MTU size. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_GetPeerMtu (UINT8 handle); + +/******************************************************************************* +** +** Function AVCT_MsgReq +** +** Description Send an AVCTP message to a peer device. In calling +** AVCT_MsgReq(), the application should keep track of the +** congestion state of AVCTP as communicated with events +** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the +** application calls AVCT_MsgReq() when AVCTP is congested +** the message may be discarded. The application may make its +** first call to AVCT_MsgReq() after it receives an +** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or +** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel. +** +** p_msg->layer_specific must be set to +** AVCT_DATA_CTRL for control channel traffic; +** AVCT_DATA_BROWSE for for browse channel traffic. +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVCT_API extern UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg); + +#ifdef __cplusplus +} +#endif + + +#endif /* AVCT_API_H */ + diff --git a/stack/include/avdt_api.h b/stack/include/avdt_api.h new file mode 100644 index 0000000..0a9b7f8 --- /dev/null +++ b/stack/include/avdt_api.h @@ -0,0 +1,902 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 interface file contains the interface to the Audio Video + * Distribution Transport Protocol (AVDTP). + * + ******************************************************************************/ +#ifndef AVDT_API_H +#define AVDT_API_H + +#include "bt_types.h" +#include "bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +#ifndef AVDT_VERSION +#define AVDT_VERSION 0x0102 +#endif +#define AVDT_VERSION_SYNC 0x0103 + +/* API function return value result codes. */ +#define AVDT_SUCCESS 0 /* Function successful */ +#define AVDT_BAD_PARAMS 1 /* Invalid parameters */ +#define AVDT_NO_RESOURCES 2 /* Not enough resources */ +#define AVDT_BAD_HANDLE 3 /* Bad handle */ +#define AVDT_BUSY 4 /* A procedure is already in progress */ +#define AVDT_WRITE_FAIL 5 /* Write failed */ + +/* The index to access the codec type in codec_info[]. */ +#define AVDT_CODEC_TYPE_INDEX 2 + +/* The size in bytes of a Adaptation Layer header. */ +#define AVDT_AL_HDR_SIZE 3 + +/* The size in bytes of a media packet header. */ +#define AVDT_MEDIA_HDR_SIZE 12 + +/* AVDTP 7.5.3 Adaptation Layer Fragmentation + * original length of the un-fragmented transport packet should be specified by + * two bytes length field of Adaptation Layer Header */ +#define AVDT_MAX_MEDIA_SIZE (0xFFFF - AVDT_MEDIA_HDR_SIZE) + +/* The handle is used when reporting MULTI_AV specific events */ +#define AVDT_MULTI_AV_HANDLE 0xFF + +/* The number of bytes needed by the protocol stack for the protocol headers +** of a media packet. This is the size of the media packet header, the +** L2CAP packet header and HCI header. +*/ +#define AVDT_MEDIA_OFFSET 23 + +/* The marker bit is used by the application to mark significant events such +** as frame boundaries in the data stream. This constant is used to check or +** set the marker bit in the m_pt parameter of an AVDT_WriteReq() +** or AVDT_DATA_IND_EVT. +*/ +#define AVDT_MARKER_SET 0x80 + +/* SEP Type. This indicates the stream endpoint type. */ +#define AVDT_TSEP_SRC 0 /* Source SEP */ +#define AVDT_TSEP_SNK 1 /* Sink SEP */ + +/* initiator/acceptor role for adaption */ +#define AVDT_INT 0 /* initiator */ +#define AVDT_ACP 1 /* acceptor */ + +/* Media Type. This indicates the media type of the stream endpoint. */ +#define AVDT_MEDIA_AUDIO 0 /* Audio SEP */ +#define AVDT_MEDIA_VIDEO 1 /* Video SEP */ +#define AVDT_MEDIA_MULTI 2 /* Multimedia SEP */ + +/* for reporting packets */ +#define AVDT_RTCP_PT_SR 200 /* the packet type - SR (Sender Report) */ +#define AVDT_RTCP_PT_RR 201 /* the packet type - RR (Receiver Report) */ +#define AVDT_RTCP_PT_SDES 202 /* the packet type - SDES (Source Description) */ +typedef UINT8 AVDT_REPORT_TYPE; + +#define AVDT_RTCP_SDES_CNAME 1 /* SDES item CNAME */ +#ifndef AVDT_MAX_CNAME_SIZE +#define AVDT_MAX_CNAME_SIZE 28 +#endif + +/* Protocol service capabilities. This indicates the protocol service +** capabilities of a stream endpoint. This value is a mask. +** Multiple values can be combined with a bitwise OR. +*/ +#define AVDT_PSC_TRANS (1<<1) /* Media transport */ +#define AVDT_PSC_REPORT (1<<2) /* Reporting */ +#define AVDT_PSC_RECOV (1<<3) /* Recovery */ +#define AVDT_PSC_HDRCMP (1<<5) /* Header compression */ +#define AVDT_PSC_MUX (1<<6) /* Multiplexing */ +#define AVDT_PSC_DELAY_RPT (1<<8) /* Delay Report */ + +/* Recovery type. This indicates the recovery type. */ +#define AVDT_RECOV_RFC2733 1 /* RFC2733 recovery */ + +/* Header compression capabilities. This indicates the header compression +** capabilities. This value is a mask. Multiple values can be combined +** with a bitwise OR. +*/ +#define AVDT_HDRCMP_MEDIA (1<<5) /* Available for media packets */ +#define AVDT_HDRCMP_RECOV (1<<6) /* Available for recovery packets */ +#define AVDT_HDRCMP_BACKCH (1<<7) /* Back channel supported */ + +/* Multiplexing capabilities mask. */ +#define AVDT_MUX_FRAG (1<<7) /* Allow Adaptation Layer Fragmentation */ + +/* Application service category. This indicates the application +** service category. +*/ +#define AVDT_ASC_PROTECT 4 /* Content protection */ +#define AVDT_ASC_CODEC 7 /* Codec */ + +/* Error codes. The following are error codes defined in the AVDTP and GAVDP +** specifications. These error codes communicate protocol errors between +** AVDTP and the application. More detailed descriptions of the error codes +** and their appropriate use can be found in the AVDTP and GAVDP specifications. +** These error codes are unrelated to the result values returned by the +** AVDTP API functions. +*/ +#define AVDT_ERR_HEADER 0x01 /* Bad packet header format */ +#define AVDT_ERR_LENGTH 0x11 /* Bad packet length */ +#define AVDT_ERR_SEID 0x12 /* Invalid SEID */ +#define AVDT_ERR_IN_USE 0x13 /* The SEP is in use */ +#define AVDT_ERR_NOT_IN_USE 0x14 /* The SEP is not in use */ +#define AVDT_ERR_CATEGORY 0x17 /* Bad service category */ +#define AVDT_ERR_PAYLOAD 0x18 /* Bad payload format */ +#define AVDT_ERR_NSC 0x19 /* Requested command not supported */ +#define AVDT_ERR_INVALID_CAP 0x1A /* Reconfigure attempted invalid capabilities */ +#define AVDT_ERR_RECOV_TYPE 0x22 /* Requested recovery type not defined */ +#define AVDT_ERR_MEDIA_TRANS 0x23 /* Media transport capability not correct */ +#define AVDT_ERR_RECOV_FMT 0x25 /* Recovery service capability not correct */ +#define AVDT_ERR_ROHC_FMT 0x26 /* Header compression service capability not correct */ +#define AVDT_ERR_CP_FMT 0x27 /* Content protection service capability not correct */ +#define AVDT_ERR_MUX_FMT 0x28 /* Multiplexing service capability not correct */ +#define AVDT_ERR_UNSUP_CFG 0x29 /* Configuration not supported */ +#define AVDT_ERR_BAD_STATE 0x31 /* Message cannot be processed in this state */ +#define AVDT_ERR_REPORT_FMT 0x65 /* Report service capability not correct */ +#define AVDT_ERR_SERVICE 0x80 /* Invalid service category */ +#define AVDT_ERR_RESOURCE 0x81 /* Insufficient resources */ +#define AVDT_ERR_INVALID_MCT 0xC1 /* Invalid Media Codec Type */ +#define AVDT_ERR_UNSUP_MCT 0xC2 /* Unsupported Media Codec Type */ +#define AVDT_ERR_INVALID_LEVEL 0xC3 /* Invalid Level */ +#define AVDT_ERR_UNSUP_LEVEL 0xC4 /* Unsupported Level */ +#define AVDT_ERR_INVALID_CP 0xE0 /* Invalid Content Protection Type */ +#define AVDT_ERR_INVALID_FORMAT 0xE1 /* Invalid Content Protection format */ + +/* Additional error codes. This indicates error codes used by AVDTP +** in addition to the ones defined in the specifications. +*/ +#define AVDT_ERR_CONNECT 0x07 /* Connection failed. */ +#define AVDT_ERR_TIMEOUT 0x08 /* Response timeout. */ + +/* Control callback events. */ +#define AVDT_DISCOVER_CFM_EVT 0 /* Discover confirm */ +#define AVDT_GETCAP_CFM_EVT 1 /* Get capabilities confirm */ +#define AVDT_OPEN_CFM_EVT 2 /* Open confirm */ +#define AVDT_OPEN_IND_EVT 3 /* Open indication */ +#define AVDT_CONFIG_IND_EVT 4 /* Configuration indication */ +#define AVDT_START_CFM_EVT 5 /* Start confirm */ +#define AVDT_START_IND_EVT 6 /* Start indication */ +#define AVDT_SUSPEND_CFM_EVT 7 /* Suspend confirm */ +#define AVDT_SUSPEND_IND_EVT 8 /* Suspend indication */ +#define AVDT_CLOSE_CFM_EVT 9 /* Close confirm */ +#define AVDT_CLOSE_IND_EVT 10 /* Close indication */ +#define AVDT_RECONFIG_CFM_EVT 11 /* Reconfiguration confirm */ +#define AVDT_RECONFIG_IND_EVT 12 /* Reconfiguration indication */ +#define AVDT_SECURITY_CFM_EVT 13 /* Security confirm */ +#define AVDT_SECURITY_IND_EVT 14 /* Security indication */ +#define AVDT_WRITE_CFM_EVT 15 /* Write confirm */ +#define AVDT_CONNECT_IND_EVT 16 /* Signaling channel connected */ +#define AVDT_DISCONNECT_IND_EVT 17 /* Signaling channel disconnected */ +#define AVDT_REPORT_CONN_EVT 18 /* Reporting channel connected */ +#define AVDT_REPORT_DISCONN_EVT 19 /* Reporting channel disconnected */ +#define AVDT_DELAY_REPORT_EVT 20 /* Delay report received */ +#define AVDT_DELAY_REPORT_CFM_EVT 21 /* Delay report response received */ + +#define AVDT_MAX_EVT (AVDT_DELAY_REPORT_CFM_EVT) + +/* PSM for AVDT */ +#define AVDT_PSM 0x0019 + +/* Nonsupported protocol command messages. This value is used in tAVDT_CS */ +#define AVDT_NSC_SUSPEND 0x01 /* Suspend command not supported */ +#define AVDT_NSC_RECONFIG 0x02 /* Reconfigure command not supported */ +#define AVDT_NSC_SECURITY 0x04 /* Security command not supported */ + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef struct +{ + UINT32 ntp_sec; /* NTP time: seconds relative to 0h UTC on 1 January 1900 */ + UINT32 ntp_frac; /* NTP time: the fractional part */ + UINT32 rtp_time; /* timestamp in RTP header */ + UINT32 pkt_count; /* sender's packet count: since starting transmission + * up until the time this SR packet was generated. */ + UINT32 octet_count; /* sender's octet count: same comment */ +} tAVDT_SENDER_INFO; + +typedef struct +{ + UINT8 frag_lost; /* fraction lost since last RR */ + UINT32 packet_lost; /* cumulative number of packets lost since the beginning */ + UINT32 seq_num_rcvd; /* extended highest sequence number received */ + UINT32 jitter; /* interarrival jitter */ + UINT32 lsr; /* last SR timestamp */ + UINT32 dlsr; /* delay since last SR */ +} tAVDT_REPORT_BLK; + +typedef union +{ + tAVDT_SENDER_INFO sr; + tAVDT_REPORT_BLK rr; + UINT8 cname[AVDT_MAX_CNAME_SIZE + 1]; +} tAVDT_REPORT_DATA; + +/* This structure contains parameters which are set at registration. */ +typedef struct { + UINT16 ctrl_mtu; /* L2CAP MTU of the AVDTP signaling channel */ + UINT8 ret_tout; /* AVDTP signaling retransmission timeout */ + UINT8 sig_tout; /* AVDTP signaling message timeout */ + UINT8 idle_tout; /* AVDTP idle signaling channel timeout */ + UINT8 sec_mask; /* Security mask for BTM_SetSecurityLevel() */ +} tAVDT_REG; + +/* This structure contains the SEP information. This information is +** transferred during the discovery procedure. +*/ +typedef struct { + BOOLEAN in_use; /* TRUE if stream is currently in use */ + UINT8 seid; /* Stream endpoint identifier */ + UINT8 media_type; /* Media type */ + UINT8 tsep; /* SEP type */ +} tAVDT_SEP_INFO; + +/* This structure contains the SEP configuration. */ +typedef struct { + UINT8 codec_info[AVDT_CODEC_SIZE]; /* Codec capabilities array */ + UINT8 protect_info[AVDT_PROTECT_SIZE]; /* Content protection capabilities */ + UINT8 num_codec; /* Number of media codec information elements */ + UINT8 num_protect; /* Number of content protection information elements */ + UINT16 psc_mask; /* Protocol service capabilities mask */ + UINT8 recov_type; /* Recovery type */ + UINT8 recov_mrws; /* Maximum recovery window size */ + UINT8 recov_mnmp; /* Recovery maximum number of media packets */ + UINT8 hdrcmp_mask; /* Header compression capabilities */ +#if AVDT_MULTIPLEXING == TRUE + UINT8 mux_mask; /* Multiplexing capabilities. AVDT_MUX_XXX bits can be combined with a bitwise OR */ + UINT8 mux_tsid_media; /* TSID for media transport session */ + UINT8 mux_tcid_media; /* TCID for media transport session */ + UINT8 mux_tsid_report; /* TSID for reporting transport session */ + UINT8 mux_tcid_report; /* TCID for reporting transport session */ + UINT8 mux_tsid_recov; /* TSID for recovery transport session */ + UINT8 mux_tcid_recov; /* TCID for recovery transport session */ +#endif +} tAVDT_CFG; + +/* Header structure for callback event parameters. */ +typedef struct { + UINT8 err_code; /* Zero if operation succeeded; nonzero if operation failed */ + UINT8 err_param; /* Error parameter included for some events */ + UINT8 label; /* Transaction label */ + UINT8 seid; /* For internal use only */ + UINT8 sig_id; /* For internal use only */ + UINT8 ccb_idx; /* For internal use only */ +} tAVDT_EVT_HDR; + +/* This data structure is associated with the AVDT_GETCAP_CFM_EVT, +** AVDT_RECONFIG_IND_EVT, and AVDT_RECONFIG_CFM_EVT. +*/ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_CFG *p_cfg; /* Pointer to configuration for this SEP */ +} tAVDT_CONFIG; + +/* This data structure is associated with the AVDT_CONFIG_IND_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_CFG *p_cfg; /* Pointer to configuration for this SEP */ + UINT8 int_seid; /* Stream endpoint ID of stream initiating the operation */ +} tAVDT_SETCONFIG; + +/* This data structure is associated with the AVDT_OPEN_IND_EVT and AVDT_OPEN_CFM_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT16 peer_mtu; /* Transport channel L2CAP MTU of the peer */ + UINT16 lcid; /* L2CAP LCID for media channel */ +} tAVDT_OPEN; + +/* This data structure is associated with the AVDT_SECURITY_IND_EVT +** and AVDT_SECURITY_CFM_EVT. +*/ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT8 *p_data; /* Pointer to security data */ + UINT16 len; /* Length in bytes of the security data */ +} tAVDT_SECURITY; + +/* This data structure is associated with the AVDT_DISCOVER_CFM_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + tAVDT_SEP_INFO *p_sep_info; /* Pointer to SEP information */ + UINT8 num_seps; /* Number of stream endpoints */ +} tAVDT_DISCOVER; + +/* This data structure is associated with the AVDT_DELAY_REPORT_EVT. */ +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT16 delay; /* Delay value */ +} tAVDT_DELAY_RPT; + +/* Union of all control callback event data structures */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_DISCOVER discover_cfm; + tAVDT_CONFIG getcap_cfm; + tAVDT_OPEN open_cfm; + tAVDT_OPEN open_ind; + tAVDT_SETCONFIG config_ind; + tAVDT_EVT_HDR start_cfm; + tAVDT_EVT_HDR suspend_cfm; + tAVDT_EVT_HDR close_cfm; + tAVDT_CONFIG reconfig_cfm; + tAVDT_CONFIG reconfig_ind; + tAVDT_SECURITY security_cfm; + tAVDT_SECURITY security_ind; + tAVDT_EVT_HDR connect_ind; + tAVDT_EVT_HDR disconnect_ind; + tAVDT_EVT_HDR report_conn; + tAVDT_DELAY_RPT delay_rpt_cmd; +} tAVDT_CTRL; + +/* This is the control callback function. This function passes control events +** to the application. This function is required for all registered stream +** endpoints and for the AVDT_DiscoverReq() and AVDT_GetCapReq() functions. +** +*/ +typedef void (tAVDT_CTRL_CBACK)(UINT8 handle, BD_ADDR bd_addr, UINT8 event, + tAVDT_CTRL *p_data); + +/* This is the data callback function. It is executed when AVDTP has a media +** packet ready for the application. This function is required for SNK +** endpoints and not applicable for SRC endpoints. +*/ +typedef void (tAVDT_DATA_CBACK)(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, + UINT8 m_pt); + +#if AVDT_MULTIPLEXING == TRUE +/* This is the second version of the data callback function. This version uses +** application buffer assigned by AVDT_SetMediaBuf. Caller can assign different +** buffer during callback or can leave the current buffer for further using. +** This callback is called when AVDTP has a media packet ready for the application. +** This function is required for SNK endpoints and not applicable for SRC endpoints. +*/ +typedef void (tAVDT_MEDIA_CBACK)(UINT8 handle, UINT8 *p_payload, UINT32 payload_len, + UINT32 time_stamp, UINT16 seq_num, UINT8 m_pt, UINT8 marker); +#endif + +#if AVDT_REPORTING == TRUE +/* This is the report callback function. It is executed when AVDTP has a reporting +** packet ready for the application. This function is required for streams +** created with AVDT_PSC_REPORT. +*/ +typedef void (tAVDT_REPORT_CBACK)(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data); +#endif + +typedef UINT16 (tAVDT_GETCAP_REQ) (BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback); + +/* This structure contains information required when a stream is created. +** It is passed to the AVDT_CreateStream() function. +*/ +typedef struct { + tAVDT_CFG cfg; /* SEP configuration */ + tAVDT_CTRL_CBACK *p_ctrl_cback; /* Control callback function */ + tAVDT_DATA_CBACK *p_data_cback; /* Data callback function */ +#if AVDT_MULTIPLEXING == TRUE + tAVDT_MEDIA_CBACK *p_media_cback; /* Media callback function. It will be called only if p_data_cback is NULL */ +#endif +#if AVDT_REPORTING == TRUE + tAVDT_REPORT_CBACK *p_report_cback;/* Report callback function. */ +#endif + UINT16 mtu; /* The L2CAP MTU of the transport channel */ + UINT16 flush_to; /* The L2CAP flush timeout of the transport channel */ + UINT8 tsep; /* SEP type */ + UINT8 media_type; /* Media type */ + UINT16 nsc_mask; /* Nonsupported protocol command messages */ +} tAVDT_CS; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVDT_Register +** +** Description This is the system level registration function for the +** AVDTP protocol. This function initializes AVDTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVDTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_Deregister +** +** Description This function is called to deregister use AVDTP protocol. +** It is called when AVDTP is no longer being used by any +** application in the system. Before this function can be +** called, all streams must be removed with AVDT_RemoveStream(). +** +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDT_Deregister(void); + +/******************************************************************************* +** +** Function AVDT_CreateStream +** +** Description Create a stream endpoint. After a stream endpoint is +** created an application can initiate a connection between +** this endpoint and an endpoint on a peer device. In +** addition, a peer device can discover, get the capabilities, +** and connect to this endpoint. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs); + +/******************************************************************************* +** +** Function AVDT_RemoveStream +** +** Description Remove a stream endpoint. This function is called when +** the application is no longer using a stream endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and then the stream endpoint +** is removed. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_RemoveStream(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_DiscoverReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and discovers +** the stream endpoints on the peer device. (Please note +** that AVDTP discovery is unrelated to SDP discovery). +** This function can be called at any time regardless of whether +** there is an AVDTP connection to the peer device. +** +** When discovery is complete, an AVDT_DISCOVER_CFM_EVT +** is sent to the application via its callback function. +** The application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again to the same device until +** discovery is complete. +** +** The memory addressed by sep_info is allocated by the +** application. This memory is written to by AVDTP as part +** of the discovery procedure. This memory must remain +** accessible until the application receives the +** AVDT_DISCOVER_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info, + UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback); + + +/******************************************************************************* +** +** Function AVDT_GetCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_GetCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_GetAllCapReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and gets the +** capabilities of a stream endpoint on the peer device. +** This function can be called at any time regardless of +** whether there is an AVDTP connection to the peer device. +** +** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is +** sent to the application via its callback function. The +** application must not call AVDT_GetCapReq() or +** AVDT_DiscoverReq() again until the procedure is complete. +** +** The memory pointed to by p_cfg is allocated by the +** application. This memory is written to by AVDTP as part +** of the get capabilities procedure. This memory must +** remain accessible until the application receives +** the AVDT_GETCAP_CFM_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_DelayReport +** +** Description This functions sends a Delay Report to the peer device +** that is associated with a particular SEID. +** This function is called by SNK device. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay); + +/******************************************************************************* +** +** Function AVDT_OpenReq +** +** Description This function initiates a connection to the AVDTP service +** on the peer device, if not already present, and connects +** to a stream endpoint on a peer device. When the connection +** is completed, an AVDT_OPEN_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, + tAVDT_CFG *p_cfg); + + +/******************************************************************************* +** +** Function AVDT_ConfigRsp +** +** Description Respond to a configure request from the peer device. This +** function must be called if the application receives an +** AVDT_CONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 category); + +/******************************************************************************* +** +** Function AVDT_StartReq +** +** Description Start one or more stream endpoints. This initiates the +** transfer of media packets for the streams. All stream +** endpoints must previously be opened. When the streams +** are started, an AVDT_START_CFM_EVT is sent to the +** application via the control callback function for each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_StartReq(UINT8 *p_handles, UINT8 num_handles); + +/******************************************************************************* +** +** Function AVDT_SuspendReq +** +** Description Suspend one or more stream endpoints. This suspends the +** transfer of media packets for the streams. All stream +** endpoints must previously be open and started. When the +** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to +** the application via the control callback function for +** each stream. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles); + +/******************************************************************************* +** +** Function AVDT_CloseReq +** +** Description Close a stream endpoint. This stops the transfer of media +** packets and closes the transport channel associated with +** this stream endpoint. When the stream is closed, an +** AVDT_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_CloseReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_ReconfigReq +** +** Description Reconfigure a stream endpoint. This allows the application +** to change the codec or content protection capabilities of +** a stream endpoint after it has been opened. This function +** can only be called if the stream is opened but not started +** or if the stream has been suspended. When the procedure +** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the +** application via the control callback function for this handle. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg); + +/******************************************************************************* +** +** Function AVDT_ReconfigRsp +** +** Description Respond to a reconfigure request from the peer device. +** This function must be called if the application receives +** an AVDT_RECONFIG_IND_EVT through its control callback. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 category); + +/******************************************************************************* +** +** Function AVDT_SecurityReq +** +** Description Send a security request to the peer device. When the +** security procedure is completed, an AVDT_SECURITY_CFM_EVT +** is sent to the application via the control callback function +** for this handle. (Please note that AVDTP security procedures +** are unrelated to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function AVDT_SecurityRsp +** +** Description Respond to a security request from the peer device. +** This function must be called if the application receives +** an AVDT_SECURITY_IND_EVT through its control callback. +** (Please note that AVDTP security procedures are unrelated +** to Bluetooth link level security.) +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code, + UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function AVDT_WriteReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteReq(). If the applications calls +** AVDT_WriteReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT +** or AVDT_START_IND_EVT. +** +** The application passes the packet using the BT_HDR structure. +** This structure is described in section 2.1. The offset +** field must be equal to or greater than AVDT_MEDIA_OFFSET. +** This allows enough space in the buffer for the L2CAP and +** AVDTP headers. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_WriteReq(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, + UINT8 m_pt); + +/******************************************************************************* +** +** Function AVDT_ConnectReq +** +** Description This function initiates an AVDTP signaling connection +** to the peer device. When the connection is completed, an +** AVDT_CONNECT_IND_EVT is sent to the application via its +** control callback function. If the connection attempt fails +** an AVDT_DISCONNECT_IND_EVT is sent. The security mask +** parameter overrides the outgoing security mask set in +** AVDT_Register(). +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, + tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_DisconnectReq +** +** Description This function disconnect an AVDTP signaling connection +** to the peer device. When disconnected an +** AVDT_DISCONNECT_IND_EVT is sent to the application via its +** control callback function. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDT_GetL2CapChannel +** +** Description Get the L2CAP CID used by the handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_GetL2CapChannel(UINT8 handle); + +/******************************************************************************* +** +** Function AVDT_GetSignalChannel +** +** Description Get the L2CAP CID used by the signal channel of the given handle. +** +** Returns CID if successful, otherwise 0. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function AVDT_WriteDataReq +** +** Description Send a media packet to the peer device. The stream must +** be started before this function is called. Also, this +** function can only be called if the stream is a SRC. +** +** When AVDTP has sent the media packet and is ready for the +** next packet, an AVDT_WRITE_CFM_EVT is sent to the +** application via the control callback. The application must +** wait for the AVDT_WRITE_CFM_EVT before it makes the next +** call to AVDT_WriteDataReq(). If the applications calls +** AVDT_WriteDataReq() before it receives the event the packet +** will not be sent. The application may make its first call +** to AVDT_WriteDataReq() after it receives an +** AVDT_START_CFM_EVT or AVDT_START_IND_EVT. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_WriteDataReq(UINT8 handle, UINT8 *p_data, UINT32 data_len, + UINT32 time_stamp, UINT8 m_pt, UINT8 marker); + +/******************************************************************************* +** +** Function AVDT_SetMediaBuf +** +** Description Assigns buffer for media packets or forbids using of assigned +** buffer if argument p_buf is NULL. This function can only +** be called if the stream is a SNK. +** +** AVDTP uses this buffer to reassemble fragmented media packets. +** When AVDTP receives a complete media packet, it calls the +** p_media_cback assigned by AVDT_CreateStream(). +** This function can be called during callback to assign a +** different buffer for next media packet or can leave the current +** buffer for next packet. +** +** Returns AVDT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len); + +/******************************************************************************* +** +** Function AVDT_SendReport +** +** Description +** +** +** +** Returns +** +*******************************************************************************/ +AVDT_API extern UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type, + tAVDT_REPORT_DATA *p_data); + +/****************************************************************************** +** +** Function AVDT_SetTraceLevel +** +** Description Sets the trace level for AVDT. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVDT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +AVDT_API extern UINT8 AVDT_SetTraceLevel (UINT8 new_level); + +#ifdef __cplusplus +} +#endif + + +#endif /* AVDT_API_H */ diff --git a/stack/include/avdtc_api.h b/stack/include/avdtc_api.h new file mode 100644 index 0000000..14a1a21 --- /dev/null +++ b/stack/include/avdtc_api.h @@ -0,0 +1,231 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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 interface file contains the interface AVDTP conformance API. These + * additional API functions and callback events are provided for + * conformance testing purposes only. They are not intended to be used by + * an application. + * + ******************************************************************************/ +#ifndef AVDT_CAPI_H +#define AVDT_CAPI_H + +#include "avdt_api.h" + +/* start AVDTC events here to distinguish from AVDT events */ +#define AVDTC_EVT_BEGIN 0x80 + +#define AVDTC_DISCOVER_IND_EVT (0 + AVDTC_EVT_BEGIN) /* Discover indication */ +#define AVDTC_GETCAP_IND_EVT (1 + AVDTC_EVT_BEGIN) /* Get capabilities indication */ +#define AVDTC_SETCONFIG_CFM_EVT (2 + AVDTC_EVT_BEGIN) /* Set configuration confirm */ +#define AVDTC_GETCONFIG_IND_EVT (3 + AVDTC_EVT_BEGIN) /* Get configuration indication */ +#define AVDTC_GETCONFIG_CFM_EVT (4 + AVDTC_EVT_BEGIN) /* Get configuration confirm */ +#define AVDTC_OPEN_IND_EVT (5 + AVDTC_EVT_BEGIN) /* Open indication */ +#define AVDTC_START_IND_EVT (6 + AVDTC_EVT_BEGIN) /* Start indication */ +#define AVDTC_CLOSE_IND_EVT (7 + AVDTC_EVT_BEGIN) /* Close indication */ +#define AVDTC_SUSPEND_IND_EVT (8 + AVDTC_EVT_BEGIN) /* Suspend indication */ +#define AVDTC_ABORT_IND_EVT (9 + AVDTC_EVT_BEGIN) /* Abort indication */ +#define AVDTC_ABORT_CFM_EVT (10 + AVDTC_EVT_BEGIN) /* Abort confirm */ + +typedef struct { + tAVDT_EVT_HDR hdr; /* Event header */ + UINT8 seid_list[AVDT_NUM_SEPS]; /* Array of SEID values */ + UINT8 num_seps; /* Number of values in array */ +} tAVDT_MULTI; + +/* Union of all control callback event data structures */ +typedef union { + tAVDT_EVT_HDR hdr; + tAVDT_CONFIG getconfig_cfm; + tAVDT_MULTI start_ind; + tAVDT_MULTI suspend_ind; +} tAVDTC_CTRL; + +typedef void tAVDTC_CTRL_CBACK(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDTC_CTRL *p_data); + +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function AVDTC_Init +** +** Description This function is called to begin using the conformance API. +** It must be called after AVDT_Register() and before any +** other API or conformance API functions are called. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_Init(tAVDTC_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function AVDTC_DiscoverRsp +** +** Description Send a discover response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_DiscoverRsp(BD_ADDR bd_addr, UINT8 label, + tAVDT_SEP_INFO sep_info[], UINT8 num_seps); + +/******************************************************************************* +** +** Function AVDTC_GetCapRsp +** +** Description Send a get capabilities response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_GetCapRsp(BD_ADDR bd_addr, UINT8 label, tAVDT_CFG *p_cap); + +/******************************************************************************* +** +** Function AVDTC_GetAllCapRsp +** +** Description Send a get all capabilities response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_GetAllCapRsp(BD_ADDR bd_addr, UINT8 label, tAVDT_CFG *p_cap); + +/******************************************************************************* +** +** Function AVDTC_GetConfigReq +** +** Description Send a get configuration request. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_GetConfigReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_GetConfigRsp +** +** Description Send a get configuration response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_GetConfigRsp(UINT8 handle, UINT8 label, tAVDT_CFG *p_cfg); + +/******************************************************************************* +** +** Function AVDTC_OpenReq +** +** Description Send an open request. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_OpenReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_OpenRsp +** +** Description Send an open response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_OpenRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_StartRsp +** +** Description Send a start response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_StartRsp(UINT8 *p_handles, UINT8 num_handles, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_CloseRsp +** +** Description Send a close response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_CloseRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_SuspendRsp +** +** Description Send a suspend response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_SuspendRsp(UINT8 *p_handles, UINT8 num_handles, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_AbortReq +** +** Description Send an abort request. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_AbortReq(UINT8 handle); + +/******************************************************************************* +** +** Function AVDTC_AbortRsp +** +** Description Send an abort response. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_AbortRsp(UINT8 handle, UINT8 label); + +/******************************************************************************* +** +** Function AVDTC_Rej +** +** Description Send a reject message. +** +** Returns void +** +*******************************************************************************/ +AVDT_API extern void AVDTC_Rej(UINT8 handle, BD_ADDR bd_addr, UINT8 cmd, UINT8 label, + UINT8 err_code, UINT8 err_param); + +#ifdef __cplusplus +} +#endif + +#endif /* AVDT_CAPI_H */ + diff --git a/stack/include/avrc_api.h b/stack/include/avrc_api.h new file mode 100644 index 0000000..1f4bb7f --- /dev/null +++ b/stack/include/avrc_api.h @@ -0,0 +1,634 @@ +/****************************************************************************** + * + * Copyright (C) 2006-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * nterface to AVRCP Application Programming Interface + * + ******************************************************************************/ +#ifndef AVRC_API_H +#define AVRC_API_H +#include "bt_target.h" +#include "avct_api.h" +#include "sdp_api.h" +#include "avrc_defs.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* API function return value result codes. */ +#define AVRC_SUCCESS AVCT_SUCCESS /* 0 Function successful */ +#define AVRC_NO_RESOURCES AVCT_NO_RESOURCES /* 1 Not enough resources */ +#define AVRC_BAD_HANDLE AVCT_BAD_HANDLE /* 2 Bad handle */ +#define AVRC_PID_IN_USE AVCT_PID_IN_USE /* 3 PID already in use */ +#define AVRC_NOT_OPEN AVCT_NOT_OPEN /* 4 Connection not open */ +#define AVRC_MSG_TOO_BIG 5 /* 5 the message length exceed the MTU of the browsing channel */ +#define AVRC_FAIL 0x10 /* 0x10 generic failure */ +#define AVRC_BAD_PARAM 0x11 /* 0x11 bad parameter */ + +/* Control role - same as AVCT_TARGET/AVCT_CONTROL */ +#define AVRC_CT_TARGET 1 /* target */ +#define AVRC_CT_CONTROL 2 /* controller */ +#define AVRC_CT_PASSIVE 4 /* If conflict, allow the other side to succeed */ + +/* Connection role */ +#define AVRC_CONN_INT AVCT_INT /* initiator */ +#define AVRC_CONN_ACP AVCT_ACP /* Acceptor */ + + +/* AVRC CTRL events */ +/* AVRC_OPEN_IND_EVT event is sent when the connection is successfully opened. + * This eventis sent in response to an AVRC_Open(). */ +#define AVRC_OPEN_IND_EVT 0 + +/* AVRC_CLOSE_IND_EVT event is sent when a connection is closed. + * This event can result from a call to AVRC_Close() or when the peer closes + * the connection. It is also sent when a connection attempted through + * AVRC_Open() fails. */ +#define AVRC_CLOSE_IND_EVT 1 + +/* AVRC_CONG_IND_EVT event indicates that AVCTP is congested and cannot send + * any more messages. */ +#define AVRC_CONG_IND_EVT 2 + +/* AVRC_UNCONG_IND_EVT event indicates that AVCTP is uncongested and ready to + * send messages. */ +#define AVRC_UNCONG_IND_EVT 3 + + /* AVRC_BROWSE_OPEN_IND_EVT event is sent when the browse channel is successfully opened. + * This eventis sent in response to an AVRC_Open() or AVRC_OpenBrowse() . */ +#define AVRC_BROWSE_OPEN_IND_EVT 4 + +/* AVRC_BROWSE_CLOSE_IND_EVT event is sent when a browse channel is closed. + * This event can result from a call to AVRC_Close(), AVRC_CloseBrowse() or when the peer closes + * the connection. It is also sent when a connection attempted through + * AVRC_OpenBrowse() fails. */ +#define AVRC_BROWSE_CLOSE_IND_EVT 5 + +/* AVRC_BROWSE_CONG_IND_EVT event indicates that AVCTP browse channel is congested and cannot send + * any more messages. */ +#define AVRC_BROWSE_CONG_IND_EVT 6 + +/* AVRC_BROWSE_UNCONG_IND_EVT event indicates that AVCTP browse channel is uncongested and ready to + * send messages. */ +#define AVRC_BROWSE_UNCONG_IND_EVT 7 + +/* Supported categories */ +#define AVRC_SUPF_CT_CAT1 0x0001 /* Category 1 */ +#define AVRC_SUPF_CT_CAT2 0x0002 /* Category 2 */ +#define AVRC_SUPF_CT_CAT3 0x0004 /* Category 3 */ +#define AVRC_SUPF_CT_CAT4 0x0008 /* Category 4 */ +#define AVRC_SUPF_CT_BROWSE 0x0040 /* Browsing */ + +#define AVRC_SUPF_TG_CAT1 0x0001 /* Category 1 */ +#define AVRC_SUPF_TG_CAT2 0x0002 /* Category 2 */ +#define AVRC_SUPF_TG_CAT3 0x0004 /* Category 3 */ +#define AVRC_SUPF_TG_CAT4 0x0008 /* Category 4 */ +#define AVRC_SUPF_TG_APP_SETTINGS 0x0010 /* Player Application Settings */ +#define AVRC_SUPF_TG_GROUP_NAVI 0x0020 /* Group Navigation */ +#define AVRC_SUPF_TG_BROWSE 0x0040 /* Browsing */ +#define AVRC_SUPF_TG_MULTI_PLAYER 0x0080 /* Muliple Media Player */ + +#define AVRC_META_SUCCESS AVRC_SUCCESS +#define AVRC_META_FAIL AVRC_FAIL +#define AVRC_METADATA_CMD 0x0000 +#define AVRC_METADATA_RESP 0x0001 + + + +/***************************************************************************** +** data type definitions +*****************************************************************************/ + +/* This data type is used in AVRC_FindService() to initialize the SDP database + * to hold the result service search. */ +typedef struct +{ + UINT32 db_len; /* Length, in bytes, of the discovery database */ + tSDP_DISCOVERY_DB *p_db; /* Pointer to the discovery database */ + UINT16 num_attr;/* The number of attributes in p_attrs */ + UINT16 *p_attrs; /* The attributes filter. If NULL, AVRCP API sets the attribute filter + * to be ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_BT_PROFILE_DESC_LIST, + * ATTR_ID_SUPPORTED_FEATURES, ATTR_ID_SERVICE_NAME and ATTR_ID_PROVIDER_NAME. + * If not NULL, the input is taken as the filter. */ +} tAVRC_SDP_DB_PARAMS; + +/* This callback function returns service discovery information to the + * application after the AVRC_FindService() API function is called. The + * implementation of this callback function must copy the p_service_name + * and p_provider_name parameters passed to it as they are not guaranteed + * to remain after the callback function exits. */ +typedef void (tAVRC_FIND_CBACK) (UINT16 status); + + +/* This is the control callback function. This function passes events + * listed in Table 20 to the application. */ +typedef void (tAVRC_CTRL_CBACK) (UINT8 handle, UINT8 event, UINT16 result, + BD_ADDR peer_addr); + + +/* This is the message callback function. It is executed when AVCTP has + * a message packet ready for the application. The implementation of this + * callback function must copy the tAVRC_MSG structure passed to it as it + * is not guaranteed to remain after the callback function exits. */ +typedef void (tAVRC_MSG_CBACK) (UINT8 handle, UINT8 label, UINT8 opcode, + tAVRC_MSG *p_msg); + +typedef struct +{ + tAVRC_CTRL_CBACK *p_ctrl_cback; /* pointer to application control callback */ + tAVRC_MSG_CBACK *p_msg_cback; /* pointer to application message callback */ + UINT32 company_id; /* the company ID */ + UINT8 conn; /* Connection role (Initiator/acceptor) */ + UINT8 control; /* Control role (Control/Target) */ +} tAVRC_CONN_CB; + + + +/***************************************************************************** +** external function declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** +** Function AVRC_AddRecord +** +** Description This function is called to build an AVRCP SDP record. +** Prior to calling this function the application must +** call SDP_CreateRecord() to create an SDP record. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** p_service_name: Pointer to a null-terminated character +** string containing the service name. +** If service name is not used set this to NULL. +** +** p_provider_name: Pointer to a null-terminated character +** string containing the provider name. +** If provider name is not used set this to NULL. +** +** categories: Supported categories. +** +** sdp_handle: SDP handle returned by SDP_CreateRecord(). +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if not enough resources to build the SDP record. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, + char *p_provider_name, UINT16 categories, UINT32 sdp_handle); + +/****************************************************************************** +** +** Function AVRC_FindService +** +** Description This function is called by the application to perform service +** discovery and retrieve AVRCP SDP record information from a +** peer device. Information is returned for the first service +** record found on the server that matches the service UUID. +** The callback function will be executed when service discovery +** is complete. There can only be one outstanding call to +** AVRC_FindService() at a time; the application must wait for +** the callback before it makes another call to the function. +** The application is responsible for allocating memory for the +** discovery database. It is recommended that the size of the +** discovery database be at least 300 bytes. The application +** can deallocate the memory after the callback function has +** executed. +** +** Input Parameters: +** service_uuid: Indicates TG(UUID_SERVCLASS_AV_REM_CTRL_TARGET) +** or CT(UUID_SERVCLASS_AV_REMOTE_CONTROL) +** +** bd_addr: BD address of the peer device. +** +** p_db: SDP discovery database parameters. +** +** p_cback: Pointer to the callback function. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_PARAMS if discovery database parameters are invalid. +** AVRC_NO_RESOURCES if there are not enough resources to +** perform the service search. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_FindService(UINT16 service_uuid, BD_ADDR bd_addr, + tAVRC_SDP_DB_PARAMS *p_db, tAVRC_FIND_CBACK *p_cback); + +/****************************************************************************** +** +** Function AVRC_Open +** +** Description This function is called to open a connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the p_ccb->stream parameter. +** The connection can be a target, a controller or for both role, +** as determined by the p_ccb->control parameter. +** By definition, a target connection is an acceptor connection +** that waits for an incoming AVCTP connection from the peer. +** The connection remains available to the application until +** the application closes it by calling AVRC_Close(). The +** application does not need to reopen the connection after an +** AVRC_CLOSE_IND_EVT is received. +** +** Input Parameters: +** p_ccb->company_id: Company Identifier. +** +** p_ccb->p_ctrl_cback: Pointer to control callback function. +** +** p_ccb->p_msg_cback: Pointer to message callback function. +** +** p_ccb->conn: AVCTP connection role. This is set to +** AVCTP_INT for initiator connections and AVCTP_ACP +** for acceptor connections. +** +** p_ccb->control: Control role. This is set to +** AVRC_CT_TARGET for target connections, AVRC_CT_CONTROL +** for control connections or (AVRC_CT_TARGET|AVRC_CT_CONTROL) +** for connections that support both roles. +** +** peer_addr: BD address of peer device. This value is +** only used for initiator connections; for acceptor +** connections it can be set to NULL. +** +** Output Parameters: +** p_handle: Pointer to handle. This parameter is only +** valid if AVRC_SUCCESS is returned. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_Open(UINT8 *p_handle, tAVRC_CONN_CB *p_ccb, + BD_ADDR_PTR peer_addr); + +/****************************************************************************** +** +** Function AVRC_Close +** +** Description Close a connection opened with AVRC_Open(). +** This function is called when the +** application is no longer using a connection. +** +** Input Parameters: +** handle: Handle of this connection. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_Close(UINT8 handle); + +/****************************************************************************** +** +** Function AVRC_OpenBrowse +** +** Description This function is called to open a browsing connection to AVCTP. +** The connection can be either an initiator or acceptor, as +** determined by the conn_role. +** The handle is returned by a previous call to AVRC_Open. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_NO_RESOURCES if there are not enough resources to open +** the connection. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_OpenBrowse(UINT8 handle, UINT8 conn_role); + +/****************************************************************************** +** +** Function AVRC_CloseBrowse +** +** Description Close a connection opened with AVRC_OpenBrowse(). +** This function is called when the +** application is no longer using a connection. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_CloseBrowse(UINT8 handle); + +/****************************************************************************** +** +** Function AVRC_MsgReq +** +** Description This function is used to send the AVRCP byte stream in p_pkt +** down to AVCTP. +** +** It is expected that p_pkt->offset is at least AVCT_MSG_OFFSET +** p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE +** p_pkt->event is AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSING +** The above BT_HDR settings are set by the AVRC_Bld* functions. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt); + +/****************************************************************************** +** +** Function AVRC_UnitCmd +** +** Description Send a UNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_UnitCmd(UINT8 handle, UINT8 label); + +/****************************************************************************** +** +** Function AVRC_SubCmd +** +** Description Send a SUBUNIT INFO command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** page: Specifies which part of the subunit type table +** is requested. For AVRCP it is typically zero. +** Value range is 0-7. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_SubCmd(UINT8 handle, UINT8 label, UINT8 page); + + +/****************************************************************************** +** +** Function AVRC_PassCmd +** +** Description Send a PASS THROUGH command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_PassCmd(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg); + +/****************************************************************************** +** +** Function AVRC_PassRsp +** +** Description Send a PASS THROUGH response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a PASS THROUGH command +** message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to PASS THROUGH message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_PassRsp(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg); + + +/****************************************************************************** +** +** Function AVRC_VendorCmd +** +** Description Send a VENDOR DEPENDENT command to the peer device. This +** function can only be called for controller role connections. +** Any response message from the peer is passed back through +** the tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_VendorCmd(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg); + + +/****************************************************************************** +** +** Function AVRC_VendorRsp +** +** Description Send a VENDOR DEPENDENT response to the peer device. This +** function can only be called for target role connections. +** This function must be called when a VENDOR DEPENDENT +** command message is received from the peer through the +** tAVRC_MSG_CBACK callback function. +** +** Input Parameters: +** handle: Handle of this connection. +** +** label: Transaction label. Must be the same value as +** passed with the command message in the callback function. +** +** p_msg: Pointer to VENDOR DEPENDENT message structure. +** +** Output Parameters: +** None. +** +** Returns AVRC_SUCCESS if successful. +** AVRC_BAD_HANDLE if handle is invalid. +** +******************************************************************************/ +AVRC_API extern UINT16 AVRC_VendorRsp(UINT8 handle, UINT8 label, tAVRC_MSG_VENDOR *p_msg); + + +/****************************************************************************** +** +** Function AVRC_SetTraceLevel +** +** Description Sets the trace level for AVRC. If 0xff is passed, the +** current trace level is returned. +** +** Input Parameters: +** new_level: The level to set the AVRC tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +******************************************************************************/ +AVRC_API extern UINT8 AVRC_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function AVRC_Init +** +** Description This function is called at stack startup to allocate the +** control block (if using dynamic memory), and initializes the +** control block and tracing level. +** +** Returns void +** +*******************************************************************************/ +AVRC_API extern void AVRC_Init(void); + +/******************************************************************************* +** +** Function AVRC_ParsCommand +** +** Description This function is used to parse the received command. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +AVRC_API extern tAVRC_STS AVRC_ParsCommand (tAVRC_MSG *p_msg, tAVRC_COMMAND *p_result, UINT8 *p_buf, UINT16 buf_len); + +/******************************************************************************* +** +** Function AVRC_ParsResponse +** +** Description This function is used to parse the received response. +** +** Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed successfully. +** Otherwise, the error code defined by AVRCP 1.4 +** +*******************************************************************************/ +AVRC_API extern tAVRC_STS AVRC_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result, UINT8 *p_buf, UINT16 buf_len); + +/******************************************************************************* +** +** Function AVRC_BldCommand +** +** Description This function builds the given AVRCP command to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the command is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +AVRC_API extern tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt); + +/******************************************************************************* +** +** Function AVRC_BldResponse +** +** Description This function builds the given AVRCP response to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +AVRC_API extern tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt); + +/************************************************************************** +** +** Function AVRC_IsValidAvcType +** +** Description Check if correct AVC type is specified +** +** Returns returns TRUE if it is valid +** +** +*******************************************************************************/ +AVRC_API extern BOOLEAN AVRC_IsValidAvcType(UINT8 pdu_id, UINT8 avc_type); + +/******************************************************************************* +** +** Function AVRC_IsValidPlayerAttr +** +** Description Check if the given attrib value is a valid one +** +** +** Returns returns TRUE if it is valid +** +*******************************************************************************/ +AVRC_API extern BOOLEAN AVRC_IsValidPlayerAttr(UINT8 attr); + +#ifdef __cplusplus +} +#endif + +#endif /* AVRC_API_H */ diff --git a/stack/include/avrc_defs.h b/stack/include/avrc_defs.h new file mode 100644 index 0000000..16ba057 --- /dev/null +++ b/stack/include/avrc_defs.h @@ -0,0 +1,1419 @@ +/****************************************************************************** + * + * Copyright (C) 2006-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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * AVRCP definition and data types + * + ******************************************************************************/ +#ifndef _AVRC_DEFS_H +#define _AVRC_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* Profile revision numbers */ +#define AVRC_REV_1_0 0x0100 +#define AVRC_REV_1_3 0x0103 +#define AVRC_REV_1_4 0x0104 + +#define AVRC_PACKET_LEN 512 /* Per the spec, you must support 512 byte RC packets */ + +#define AVRC_MIN_CONTROL_MTU 48 /* Per the spec, minimum MTU for the control channel */ +#define AVRC_MIN_BROWSE_MTU 335 /* Per the spec, minimum MTU for the browsing channel */ + +#define AVRC_META_PDU_OFFSET 4 +#define AVRC_SUB_TYPE_LEN 4 +#define AVRC_UID_SIZE 8 +#define AVRC_FEATURE_MASK_SIZE 16 + +/* command type codes */ +#define AVRC_CMD_CTRL 0 /* Instruct a target to perform an operation */ +#define AVRC_CMD_STATUS 1 /* Check a device’s current status */ +#define AVRC_CMD_SPEC_INQ 2 /* Check whether a target supports a particular + control command; all operands are included */ +#define AVRC_CMD_NOTIF 3 /* Used for receiving notification of a change in a device’s state */ +#define AVRC_CMD_GEN_INQ 4 /* Check whether a target supports a particular + control command; operands are not included */ + +/* response type codes */ +#define AVRC_RSP_NOT_IMPL 8 /* The target does not implement the command specified + by the opcode and operand, + or doesn’t implement the specified subunit */ +#define AVRC_RSP_ACCEPT 9 /* The target executed or is executing the command */ +#define AVRC_RSP_REJ 10 /* The target implements the command specified by the + opcode but cannot respond because the current state + of the target doesn’t allow it */ +#define AVRC_RSP_IN_TRANS 11 /* The target implements the status command but it is + in a state of transition; the status command may + be retried at a future time */ +#define AVRC_RSP_IMPL_STBL 12 /* For specific inquiry or general inquiy commands, + the target implements the command; for status + commands, the target returns stable and includes + the status results */ +#define AVRC_RSP_CHANGED 13 /* The response frame contains a notification that the + target device’s state has changed */ +#define AVRC_RSP_INTERIM 15 /* For control commands, the target has accepted the + request but cannot return information within 100 + milliseconds; for notify commands, the target accepted + the command, and will notify the controller of a change + of target state at a future time */ + +/* subunit type */ +#define AVRC_SUB_MONITOR 0x00 /* Monitor */ +#define AVRC_SUB_AUDIO 0x01 /* Audio */ +#define AVRC_SUB_PRINTER 0x02 /* Printer */ +#define AVRC_SUB_DISC 0x03 /* Disc */ +#define AVRC_SUB_TAPE 0x04 /* Tape recorder/player */ +#define AVRC_SUB_TUNER 0x05 /* Tuner */ +#define AVRC_SUB_CA 0x06 /* CA */ +#define AVRC_SUB_CAMERA 0x07 /* Camera */ +#define AVRC_SUB_PANEL 0x09 /* Panel */ +#define AVRC_SUB_BB 0x0A /* Bulletin Board */ +#define AVRC_SUB_CAM_STOR 0x0B /* Camera Storage */ +#define AVRC_SUB_VENDOR 0x1C /* Vendor unique */ +#define AVRC_SUB_EXT 0x1E /* Subunit type extended to next byte */ +#define AVRC_SUB_UNIT 0x1F /* Unit */ + +/* opcodes - defined by 1394ta */ +#define AVRC_OP_UNIT_INFO 0x30 /* Report unit information */ +#define AVRC_OP_SUB_INFO 0x31 /* Report subunit information */ +#define AVRC_OP_VENDOR 0x00 /* Vendor-dependent commands */ +#define AVRC_OP_PASS_THRU 0x7C /* panel subunit opcode */ +/* opcodes 80-9F and E0-FF are not used by 1394ta.Sneak one for the browsing channel */ +#define AVRC_OP_BROWSE 0xFF /* Browsing */ +#define AVRC_OP_INVALID 0xFE /* invalid one */ + +/* Company ID's +*/ +#define AVRC_CO_BLUETOOTH_SIG 0x00FFFFFF +#define AVRC_CO_WIDCOMM 0x00000361 +#define AVRC_CO_BROADCOM 0x00001018 +#define AVRC_CO_METADATA 0x00001958 /* Unique COMPANY ID for Metadata messages */ + +/* State flag for Passthrough commands +*/ +#define AVRC_STATE_PRESS 0 +#define AVRC_STATE_RELEASE 1 + +/* Operation ID list for Passthrough commands +*/ +#define AVRC_ID_SELECT 0x00 /* select */ +#define AVRC_ID_UP 0x01 /* up */ +#define AVRC_ID_DOWN 0x02 /* down */ +#define AVRC_ID_LEFT 0x03 /* left */ +#define AVRC_ID_RIGHT 0x04 /* right */ +#define AVRC_ID_RIGHT_UP 0x05 /* right-up */ +#define AVRC_ID_RIGHT_DOWN 0x06 /* right-down */ +#define AVRC_ID_LEFT_UP 0x07 /* left-up */ +#define AVRC_ID_LEFT_DOWN 0x08 /* left-down */ +#define AVRC_ID_ROOT_MENU 0x09 /* root menu */ +#define AVRC_ID_SETUP_MENU 0x0A /* setup menu */ +#define AVRC_ID_CONT_MENU 0x0B /* contents menu */ +#define AVRC_ID_FAV_MENU 0x0C /* favorite menu */ +#define AVRC_ID_EXIT 0x0D /* exit */ +#define AVRC_ID_0 0x20 /* 0 */ +#define AVRC_ID_1 0x21 /* 1 */ +#define AVRC_ID_2 0x22 /* 2 */ +#define AVRC_ID_3 0x23 /* 3 */ +#define AVRC_ID_4 0x24 /* 4 */ +#define AVRC_ID_5 0x25 /* 5 */ +#define AVRC_ID_6 0x26 /* 6 */ +#define AVRC_ID_7 0x27 /* 7 */ +#define AVRC_ID_8 0x28 /* 8 */ +#define AVRC_ID_9 0x29 /* 9 */ +#define AVRC_ID_DOT 0x2A /* dot */ +#define AVRC_ID_ENTER 0x2B /* enter */ +#define AVRC_ID_CLEAR 0x2C /* clear */ +#define AVRC_ID_CHAN_UP 0x30 /* channel up */ +#define AVRC_ID_CHAN_DOWN 0x31 /* channel down */ +#define AVRC_ID_PREV_CHAN 0x32 /* previous channel */ +#define AVRC_ID_SOUND_SEL 0x33 /* sound select */ +#define AVRC_ID_INPUT_SEL 0x34 /* input select */ +#define AVRC_ID_DISP_INFO 0x35 /* display information */ +#define AVRC_ID_HELP 0x36 /* help */ +#define AVRC_ID_PAGE_UP 0x37 /* page up */ +#define AVRC_ID_PAGE_DOWN 0x38 /* page down */ +#define AVRC_ID_POWER 0x40 /* power */ +#define AVRC_ID_VOL_UP 0x41 /* volume up */ +#define AVRC_ID_VOL_DOWN 0x42 /* volume down */ +#define AVRC_ID_MUTE 0x43 /* mute */ +#define AVRC_ID_PLAY 0x44 /* play */ +#define AVRC_ID_STOP 0x45 /* stop */ +#define AVRC_ID_PAUSE 0x46 /* pause */ +#define AVRC_ID_RECORD 0x47 /* record */ +#define AVRC_ID_REWIND 0x48 /* rewind */ +#define AVRC_ID_FAST_FOR 0x49 /* fast forward */ +#define AVRC_ID_EJECT 0x4A /* eject */ +#define AVRC_ID_FORWARD 0x4B /* forward */ +#define AVRC_ID_BACKWARD 0x4C /* backward */ +#define AVRC_ID_ANGLE 0x50 /* angle */ +#define AVRC_ID_SUBPICT 0x51 /* subpicture */ +#define AVRC_ID_F1 0x71 /* F1 */ +#define AVRC_ID_F2 0x72 /* F2 */ +#define AVRC_ID_F3 0x73 /* F3 */ +#define AVRC_ID_F4 0x74 /* F4 */ +#define AVRC_ID_F5 0x75 /* F5 */ +#define AVRC_ID_VENDOR 0x7E /* vendor unique */ +#define AVRC_KEYPRESSED_RELEASE 0x80 + +/***************************************************************************** +** Metadata transfer definitions +*****************************************************************************/ + +/* Define the Metadata Packet types +*/ +#define AVRC_PKT_SINGLE 0 +#define AVRC_PKT_START 1 +#define AVRC_PKT_CONTINUE 2 +#define AVRC_PKT_END 3 +#define AVRC_PKT_TYPE_MASK 3 + +/* Define the PDUs carried in the vendor dependant data +*/ +#define AVRC_PDU_GET_CAPABILITIES 0x10 +#define AVRC_PDU_LIST_PLAYER_APP_ATTR 0x11 +#define AVRC_PDU_LIST_PLAYER_APP_VALUES 0x12 +#define AVRC_PDU_GET_CUR_PLAYER_APP_VALUE 0x13 +#define AVRC_PDU_SET_PLAYER_APP_VALUE 0x14 +#define AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT 0x15 +#define AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT 0x16 +#define AVRC_PDU_INFORM_DISPLAY_CHARSET 0x17 +#define AVRC_PDU_INFORM_BATTERY_STAT_OF_CT 0x18 +#define AVRC_PDU_GET_ELEMENT_ATTR 0x20 +#define AVRC_PDU_GET_PLAY_STATUS 0x30 +#define AVRC_PDU_REGISTER_NOTIFICATION 0x31 +#define AVRC_PDU_REQUEST_CONTINUATION_RSP 0x40 +#define AVRC_PDU_ABORT_CONTINUATION_RSP 0x41 +/* added in 1.4 */ +#define AVRC_PDU_SET_ABSOLUTE_VOLUME 0x50 +#define AVRC_PDU_SET_ADDRESSED_PLAYER 0x60 +#define AVRC_PDU_SET_BROWSED_PLAYER 0x70 +#define AVRC_PDU_GET_FOLDER_ITEMS 0x71 +#define AVRC_PDU_CHANGE_PATH 0x72 +#define AVRC_PDU_GET_ITEM_ATTRIBUTES 0x73 +#define AVRC_PDU_PLAY_ITEM 0x74 +#define AVRC_PDU_SEARCH 0x80 +#define AVRC_PDU_ADD_TO_NOW_PLAYING 0x90 +#define AVRC_PDU_GENERAL_REJECT 0xA0 + +/* Define the vendor unique id carried in the pass through data +*/ +#define AVRC_PDU_NEXT_GROUP 0x00 +#define AVRC_PDU_PREV_GROUP 0x01 +/* the only pass through vendor unique commands defined by AVRC is the group navigation commands + * The len for vendor unique data is 5 */ +#define AVRC_PASS_THRU_GROUP_LEN 5 + +#define AVRC_PDU_INVALID 0xff +/* 6.15.3 error status code for general reject */ +#define AVRC_STS_BAD_CMD 0x00 /* Invalid command, sent if TG received a PDU that it did not understand. */ +#define AVRC_STS_BAD_PARAM 0x01 /* Invalid parameter, sent if the TG received a PDU with a parameter ID that it did not understand. Sent if there is only one parameter ID in the PDU. */ +#define AVRC_STS_NOT_FOUND 0x02 /* Specified parameter not found., sent if the parameter ID is understood, but content is wrong or corrupted. */ +#define AVRC_STS_INTERNAL_ERR 0x03 /* Internal Error, sent if there are error conditions not covered by a more specific error code. */ +#define AVRC_STS_NO_ERROR 0x04 /* Operation completed without error. This is the status that should be returned if the operation was successful. */ +#define AVRC_STS_UID_CHANGED 0x05 /* UID Changed - The UIDs on the device have changed */ +/* #define AVRC_STS_GEN_ERROR 0x06 Unknown Error - this is changed to "reserved" */ +#define AVRC_STS_BAD_DIR 0x07 /* Invalid Direction - The Direction parameter is invalid - Change Path*/ +#define AVRC_STS_NOT_DIR 0x08 /* Not a Directory - The UID provided does not refer to a folder item Change Path*/ +#define AVRC_STS_NOT_EXIST 0x09 /* Does Not Exist - The UID provided does not refer to any item Change Path, PlayItem, AddToNowPlaying, GetItemAttributes*/ +#define AVRC_STS_BAD_SCOPE 0x0a /* Invalid Scope - The scope parameter is invalid GetFolderItems, PlayItem, AddToNowPlayer, GetItemAttributes, */ +#define AVRC_STS_BAD_RANGE 0x0b /* Range Out of Bounds - The start of range provided is not valid GetFolderItems*/ +#define AVRC_STS_UID_IS_DIR 0x0c /* UID is a Directory - The UID provided refers to a directory, which cannot be handled by this media player PlayItem, AddToNowPlaying */ +#define AVRC_STS_IN_USE 0x0d /* Media in Use - The media is not able to be used for this operation at this time PlayItem, AddToNowPlaying */ +#define AVRC_STS_NOW_LIST_FULL 0x0e /* Now Playing List Full - No more items can be added to the Now Playing List AddToNowPlaying*/ +#define AVRC_STS_SEARCH_NOT_SUP 0x0f /* Search Not Supported - The Browsed Media Player does not support search Search */ +#define AVRC_STS_SEARCH_BUSY 0x10 /* Search in Progress - A search operation is already in progress Search*/ +#define AVRC_STS_BAD_PLAYER_ID 0x11 /* Invalid Player Id - The specified Player Id does not refer to a valid player SetAddressedPlayer, SetBrowsedPlayer*/ +#define AVRC_STS_PLAYER_N_BR 0x12 /* Player Not Browsable - The Player Id supplied refers to a Media Player which does not support browsing. SetBrowsedPlayer */ +#define AVRC_STS_PLAYER_N_ADDR 0x13 /* Player Not Addressed. The Player Id supplied refers to a player which is not currently addressed, and the command is not able to be performed if the player is not set as addressed. Search, SetBrowsedPlayer*/ +#define AVRC_STS_BAD_SEARCH_RES 0x14 /* No valid Search Results - The Search result list does not contain valid entries, e.g. after being invalidated due to change of browsed player GetFolderItems */ +#define AVRC_STS_NO_AVAL_PLAYER 0x15 /* No available players ALL */ +#define AVRC_STS_ADDR_PLAYER_CHG 0x16 /* Addressed Player Changed - Register Notification */ +typedef UINT8 tAVRC_STS; + + +/* Define the Capability IDs +*/ +#define AVRC_CAP_COMPANY_ID 0x02 +#define AVRC_CAP_EVENTS_SUPPORTED 0x03 +#define AVRC_COMPANY_ID_LEN 3 +#define AVRC_CAPABILITY_OFFSET 2 + +/* Define the Player Application Settings IDs +*/ +#define AVRC_PLAYER_SETTING_EQUALIZER 0x01 +#define AVRC_PLAYER_SETTING_REPEAT 0x02 +#define AVRC_PLAYER_SETTING_SHUFFLE 0x03 +#define AVRC_PLAYER_SETTING_SCAN 0x04 +#define AVRC_PLAYER_SETTING_LOW_MENU_EXT 0x80 +#define AVRC_PLAYER_SETTING_HIGH_MENU_EXT 0xff + +/* Define the possible values of the Player Application Settings +*/ +#define AVRC_PLAYER_VAL_OFF 0x01 +#define AVRC_PLAYER_VAL_ON 0x02 +#define AVRC_PLAYER_VAL_SINGLE_REPEAT 0x02 +#define AVRC_PLAYER_VAL_ALL_REPEAT 0x03 +#define AVRC_PLAYER_VAL_GROUP_REPEAT 0x04 +#define AVRC_PLAYER_VAL_ALL_SHUFFLE 0x02 +#define AVRC_PLAYER_VAL_GROUP_SHUFFLE 0x03 +#define AVRC_PLAYER_VAL_ALL_SCAN 0x02 +#define AVRC_PLAYER_VAL_GROUP_SCAN 0x03 + +/* Define the possible values of Battery Status PDU +*/ +#define AVRC_BATTERY_STATUS_NORMAL 0x00 +#define AVRC_BATTERY_STATUS_WARNING 0x01 +#define AVRC_BATTERY_STATUS_CRITICAL 0x02 +#define AVRC_BATTERY_STATUS_EXTERNAL 0x03 +#define AVRC_BATTERY_STATUS_FULL_CHARGE 0x04 +typedef UINT8 tAVRC_BATTERY_STATUS; + +/* Define character set */ +#define AVRC_CHAR_SET_SIZE 2 + +/* Define the Media Attribute IDs +*/ +#define AVRC_MEDIA_ATTR_ID_TITLE 0x00000001 +#define AVRC_MEDIA_ATTR_ID_ARTIST 0x00000002 +#define AVRC_MEDIA_ATTR_ID_ALBUM 0x00000003 +#define AVRC_MEDIA_ATTR_ID_TRACK_NUM 0x00000004 +#define AVRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005 +#define AVRC_MEDIA_ATTR_ID_GENRE 0x00000006 +#define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */ +#define AVRC_MAX_NUM_MEDIA_ATTR_ID 7 + +/* Define the possible values of play state +*/ +#define AVRC_PLAYSTATE_RESP_MSG_SIZE 9 +#define AVRC_PLAYSTATE_STOPPED 0x00 /* Stopped */ +#define AVRC_PLAYSTATE_PLAYING 0x01 /* Playing */ +#define AVRC_PLAYSTATE_PAUSED 0x02 /* Paused */ +#define AVRC_PLAYSTATE_FWD_SEEK 0x03 /* Fwd Seek*/ +#define AVRC_PLAYSTATE_REV_SEEK 0x04 /* Rev Seek*/ +#define AVRC_PLAYSTATE_ERROR 0xFF /* Error */ +typedef UINT8 tAVRC_PLAYSTATE; + +/* Define the events that can be registered for notifications +*/ +#define AVRC_EVT_PLAY_STATUS_CHANGE 0x01 +#define AVRC_EVT_TRACK_CHANGE 0x02 +#define AVRC_EVT_TRACK_REACHED_END 0x03 +#define AVRC_EVT_TRACK_REACHED_START 0x04 +#define AVRC_EVT_PLAY_POS_CHANGED 0x05 +#define AVRC_EVT_BATTERY_STATUS_CHANGE 0x06 +#define AVRC_EVT_SYSTEM_STATUS_CHANGE 0x07 +#define AVRC_EVT_APP_SETTING_CHANGE 0x08 +/* added in AVRCP 1.4 */ +#define AVRC_EVT_NOW_PLAYING_CHANGE 0x09 +#define AVRC_EVT_AVAL_PLAYERS_CHANGE 0x0a +#define AVRC_EVT_ADDR_PLAYER_CHANGE 0x0b +#define AVRC_EVT_UIDS_CHANGE 0x0c +#define AVRC_EVT_VOLUME_CHANGE 0x0d + +/* the number of events that can be registered for notifications */ +#define AVRC_NUM_NOTIF_EVENTS 0x0d + +#define AVRC_EVT_MSG_LEN_1 0x01 +#define AVRC_EVT_MSG_LEN_2 0x02 +#define AVRC_EVT_MSG_LEN_5 0x05 +#define AVRC_EVT_MSG_LEN_9 0x09 + +#define AVRC_MAX_VOLUME 0x7F + +/* Define the possible values of system status +*/ +#define AVRC_SYSTEMSTATE_PWR_ON 0x00 +#define AVRC_SYSTEMSTATE_PWR_OFF 0x01 +#define AVRC_SYSTEMSTATE_PWR_UNPLUGGED 0x02 +typedef UINT8 tAVRC_SYSTEMSTATE; + +/* the frequently used character set ids */ +#define AVRC_CHARSET_ID_ASCII ((UINT16) 0x0003) /* ASCII */ +#define AVRC_CHARSET_ID_UTF8 ((UINT16) 0x006a) /* UTF-8 */ +#define AVRC_CHARSET_ID_UTF16 ((UINT16) 0x03f7) /* 1015 */ +#define AVRC_CHARSET_ID_UTF32 ((UINT16) 0x03f9) /* 1017 */ + +/***************************************************************************** +** Advanced Control +*****************************************************************************/ +#define AVRC_ITEM_PLAYER 0x01 +#define AVRC_ITEM_FOLDER 0x02 +#define AVRC_ITEM_MEDIA 0x03 + +#define AVRC_SCOPE_PLAYER_LIST 0x00 /* Media Player Item - Contains all available media players */ +#define AVRC_SCOPE_FILE_SYSTEM 0x01 /* Folder Item, Media Element Item + - The virtual filesystem containing the media content of the browsed player */ +#define AVRC_SCOPE_SEARCH 0x02 /* Media Element Item The results of a search operation on the browsed player */ +#define AVRC_SCOPE_NOW_PLAYING 0x03 /* Media Element Item The Now Playing list (or queue) of the addressed player */ + +#define AVRC_FOLDER_ITEM_COUNT_NONE 0xFF + +/* folder type */ +#define AVRC_FOLDER_TYPE_MIXED 0x00 +#define AVRC_FOLDER_TYPE_TITLES 0x01 +#define AVRC_FOLDER_TYPE_ALNUMS 0x02 +#define AVRC_FOLDER_TYPE_ARTISTS 0x03 +#define AVRC_FOLDER_TYPE_GENRES 0x04 +#define AVRC_FOLDER_TYPE_PLAYLISTS 0x05 +#define AVRC_FOLDER_TYPE_YEARS 0x06 + +/* major player type */ +#define AVRC_MJ_TYPE_AUDIO 0x01 /* Audio */ +#define AVRC_MJ_TYPE_VIDEO 0x02 /* Video */ +#define AVRC_MJ_TYPE_BC_AUDIO 0x04 /* Broadcasting Audio */ +#define AVRC_MJ_TYPE_BC_VIDEO 0x08 /* Broadcasting Video */ +#define AVRC_MJ_TYPE_INVALID 0xF0 + +/* player sub type */ +#define AVRC_SUB_TYPE_NONE 0x00 +#define AVRC_SUB_TYPE_AUDIO_BOOK 0x01 /* Audio Book */ +#define AVRC_SUB_TYPE_PODCAST 0x02 /* Podcast */ +#define AVRC_SUB_TYPE_INVALID 0xFC + +/* media item - media type */ +#define AVRC_MEDIA_TYPE_AUDIO 0x00 +#define AVRC_MEDIA_TYPE_VIDEO 0x01 + +#define AVRC_DIR_UP 0x00 /* Folder Up */ +#define AVRC_DIR_DOWN 0x01 /* Folder Down */ + +#define AVRC_UID_SIZE 8 +typedef UINT8 tAVRC_UID[AVRC_UID_SIZE]; + +/***************************************************************************** +** player attribute - supported features +*****************************************************************************/ +#define AVRC_PF_SELECT_BIT_NO 0 +#define AVRC_PF_SELECT_MASK 0x01 +#define AVRC_PF_SELECT_OFF 0 +#define AVRC_PF_SELECT_SUPPORTED(x) ((x)[AVRC_PF_SELECT_OFF] & AVRC_PF_SELECT_MASK) + +#define AVRC_PF_UP_BIT_NO 1 +#define AVRC_PF_UP_MASK 0x02 +#define AVRC_PF_UP_OFF 0 +#define AVRC_PF_UP_SUPPORTED(x) ((x)[AVRC_PF_UP_OFF] & AVRC_PF_UP_MASK) + +#define AVRC_PF_DOWN_BIT_NO 2 +#define AVRC_PF_DOWN_MASK 0x04 +#define AVRC_PF_DOWN_OFF 0 +#define AVRC_PF_DOWN_SUPPORTED(x) ((x)[AVRC_PF_DOWN_OFF] & AVRC_PF_DOWN_MASK) + +#define AVRC_PF_LEFT_BIT_NO 3 +#define AVRC_PF_LEFT_MASK 0x08 +#define AVRC_PF_LEFT_OFF 0 +#define AVRC_PF_LEFT_SUPPORTED(x) ((x)[AVRC_PF_LEFT_OFF] & AVRC_PF_LEFT_MASK) + +#define AVRC_PF_RIGHT_BIT_NO 4 +#define AVRC_PF_RIGHT_MASK 0x10 +#define AVRC_PF_RIGHT_OFF 0 +#define AVRC_PF_RIGHT_SUPPORTED(x) ((x)[AVRC_PF_RIGHT_OFF] & AVRC_PF_RIGHT_MASK) + +#define AVRC_PF_RIGHTUP_BIT_NO 5 +#define AVRC_PF_RIGHTUP_MASK 0x20 +#define AVRC_PF_RIGHTUP_OFF 0 +#define AVRC_PF_RIGHTUP_SUPPORTED(x) ((x)[AVRC_PF_RIGHTUP_OFF] & AVRC_PF_RIGHTUP_MASK) + +#define AVRC_PF_RIGHTDOWN_BIT_NO 6 +#define AVRC_PF_RIGHTDOWN_MASK 0x40 +#define AVRC_PF_RIGHTDOWN_OFF 0 +#define AVRC_PF_RIGHTDOWN_SUPPORTED(x) ((x)[AVRC_PF_RIGHTDOWN_OFF] & AVRC_PF_RIGHTDOWN_MASK) + +#define AVRC_PF_LEFTUP_BIT_NO 7 +#define AVRC_PF_LEFTUP_MASK 0x80 +#define AVRC_PF_LEFTUP_OFF 0 +#define AVRC_PF_LEFTUP_SUPPORTED(x) ((x)[AVRC_PF_LEFTUP_OFF] & AVRC_PF_LEFTUP_MASK) + +#define AVRC_PF_LEFTDOWN_BIT_NO 8 +#define AVRC_PF_LEFTDOWN_MASK 0x01 +#define AVRC_PF_LEFTDOWN_OFF 1 +#define AVRC_PF_LEFTDOWN_SUPPORTED(x) ((x)[AVRC_PF_LEFTDOWN_OFF] & AVRC_PF_LEFTDOWN_MASK) + +#define AVRC_PF_ROOT_MENU_BIT_NO 9 +#define AVRC_PF_ROOT_MENU_MASK 0x02 +#define AVRC_PF_ROOT_MENU_OFF 1 +#define AVRC_PF_ROOT_MENU_SUPPORTED(x) ((x)[AVRC_PF_ROOT_MENU_OFF] & AVRC_PF_ROOT_MENU_MASK) + +#define AVRC_PF_SETUP_MENU_BIT_NO 10 +#define AVRC_PF_SETUP_MENU_MASK 0x04 +#define AVRC_PF_SETUP_MENU_OFF 1 +#define AVRC_PF_SETUP_MENU_SUPPORTED(x) ((x)[AVRC_PF_SETUP_MENU_OFF] & AVRC_PF_SETUP_MENU_MASK) + +#define AVRC_PF_CONTENTS_MENU_BIT_NO 11 +#define AVRC_PF_CONTENTS_MENU_MASK 0x08 +#define AVRC_PF_CONTENTS_MENU_OFF 1 +#define AVRC_PF_CONTENTS_MENU_SUPPORTED(x) ((x)[AVRC_PF_CONTENTS_MENU_OFF] & AVRC_PF_CONTENTS_MENU_MASK) + +#define AVRC_PF_FAVORITE_MENU_BIT_NO 12 +#define AVRC_PF_FAVORITE_MENU_MASK 0x10 +#define AVRC_PF_FAVORITE_MENU_OFF 1 +#define AVRC_PF_FAVORITE_MENU_SUPPORTED(x) ((x)[AVRC_PF_FAVORITE_MENU_OFF] & AVRC_PF_FAVORITE_MENU_MASK) + +#define AVRC_PF_EXIT_BIT_NO 13 +#define AVRC_PF_EXIT_MASK 0x20 +#define AVRC_PF_EXIT_OFF 1 +#define AVRC_PF_EXIT_SUPPORTED(x) ((x)[AVRC_PF_EXIT_OFF] & AVRC_PF_EXIT_MASK) + +#define AVRC_PF_0_BIT_NO 14 +#define AVRC_PF_0_MASK 0x40 +#define AVRC_PF_0_OFF 1 +#define AVRC_PF_0_SUPPORTED(x) ((x)[AVRC_PF_0_OFF] & AVRC_PF_0_MASK) + +#define AVRC_PF_1_BIT_NO 15 +#define AVRC_PF_1_MASK 0x80 +#define AVRC_PF_1_OFF 1 +#define AVRC_PF_1_SUPPORTED(x) ((x)[AVRC_PF_1_OFF] & AVRC_PF_1_MASK) + +#define AVRC_PF_2_BIT_NO 16 +#define AVRC_PF_2_MASK 0x01 +#define AVRC_PF_2_OFF 2 +#define AVRC_PF_2_SUPPORTED(x) ((x)[AVRC_PF_2_OFF] & AVRC_PF_2_MASK) + +#define AVRC_PF_3_BIT_NO 17 +#define AVRC_PF_3_MASK 0x02 +#define AVRC_PF_3_OFF 2 +#define AVRC_PF_3_SUPPORTED(x) ((x)[AVRC_PF_3_OFF] & AVRC_PF_3_MASK) + +#define AVRC_PF_4_BIT_NO 18 +#define AVRC_PF_4_MASK 0x04 +#define AVRC_PF_4_OFF 2 +#define AVRC_PF_4_SUPPORTED(x) ((x)[AVRC_PF_4_OFF] & AVRC_PF_4_MASK) + +#define AVRC_PF_5_BIT_NO 19 +#define AVRC_PF_5_MASK 0x08 +#define AVRC_PF_5_OFF 2 +#define AVRC_PF_5_SUPPORTED(x) ((x)[AVRC_PF_5_OFF] & AVRC_PF_5_MASK) + +#define AVRC_PF_6_BIT_NO 20 +#define AVRC_PF_6_MASK 0x10 +#define AVRC_PF_6_OFF 2 +#define AVRC_PF_6_SUPPORTED(x) ((x)[AVRC_PF_6_OFF] & AVRC_PF_6_MASK) + +#define AVRC_PF_7_BIT_NO 21 +#define AVRC_PF_7_MASK 0x20 +#define AVRC_PF_7_OFF 2 +#define AVRC_PF_7_SUPPORTED(x) ((x)[AVRC_PF_7_OFF] & AVRC_PF_7_MASK) + +#define AVRC_PF_8_BIT_NO 22 +#define AVRC_PF_8_MASK 0x40 +#define AVRC_PF_8_OFF 2 +#define AVRC_PF_8_SUPPORTED(x) ((x)[AVRC_PF_8_OFF] & AVRC_PF_8_MASK) + +#define AVRC_PF_9_BIT_NO 23 +#define AVRC_PF_9_MASK 0x80 +#define AVRC_PF_9_OFF 2 +#define AVRC_PF_9_SUPPORTED(x) ((x)[AVRC_PF_9_OFF] & AVRC_PF_9_MASK) + +#define AVRC_PF_DOT_BIT_NO 24 +#define AVRC_PF_DOT_MASK 0x01 +#define AVRC_PF_DOT_OFF 3 +#define AVRC_PF_DOT_SUPPORTED(x) ((x)[AVRC_PF_DOT_OFF] & AVRC_PF_DOT_MASK) + +#define AVRC_PF_ENTER_BIT_NO 25 +#define AVRC_PF_ENTER_MASK 0x02 +#define AVRC_PF_ENTER_OFF 3 +#define AVRC_PF_ENTER_SUPPORTED(x) ((x)[AVRC_PF_ENTER_OFF] & AVRC_PF_ENTER_MASK) + +#define AVRC_PF_CLEAR_BIT_NO 26 +#define AVRC_PF_CLEAR_MASK 0x04 +#define AVRC_PF_CLEAR_OFF 3 +#define AVRC_PF_CLEAR_SUPPORTED(x) ((x)[AVRC_PF_CLEAR_OFF] & AVRC_PF_CLEAR_MASK) + +#define AVRC_PF_CHNL_UP_BIT_NO 27 +#define AVRC_PF_CHNL_UP_MASK 0x08 +#define AVRC_PF_CHNL_UP_OFF 3 +#define AVRC_PF_CHNL_UP_SUPPORTED(x) ((x)[AVRC_PF_CHNL_UP_OFF] & AVRC_PF_CHNL_UP_MASK) + +#define AVRC_PF_CHNL_DOWN_BIT_NO 28 +#define AVRC_PF_CHNL_DOWN_MASK 0x10 +#define AVRC_PF_CHNL_DOWN_OFF 3 +#define AVRC_PF_CHNL_DOWN_SUPPORTED(x) ((x)[AVRC_PF_CHNL_DOWN_OFF] & AVRC_PF_CHNL_DOWN_MASK) + +#define AVRC_PF_PREV_CHNL_BIT_NO 29 +#define AVRC_PF_PREV_CHNL_MASK 0x20 +#define AVRC_PF_PREV_CHNL_OFF 3 +#define AVRC_PF_PREV_CHNL_SUPPORTED(x) ((x)[AVRC_PF_PREV_CHNL_OFF] & AVRC_PF_PREV_CHNL_MASK) + +#define AVRC_PF_SOUND_SEL_BIT_NO 30 +#define AVRC_PF_SOUND_SEL_MASK 0x40 +#define AVRC_PF_SOUND_SEL_OFF 3 +#define AVRC_PF_SOUND_SEL_SUPPORTED(x) ((x)[AVRC_PF_SOUND_SEL_OFF] & AVRC_PF_SOUND_SEL_MASK) + +#define AVRC_PF_INPUT_SEL_BIT_NO 31 +#define AVRC_PF_INPUT_SEL_MASK 0x80 +#define AVRC_PF_INPUT_SEL_OFF 3 +#define AVRC_PF_INPUT_SEL_SUPPORTED(x) ((x)[AVRC_PF_INPUT_SEL_OFF] & AVRC_PF_INPUT_SEL_MASK) + +#define AVRC_PF_DISP_INFO_BIT_NO 32 +#define AVRC_PF_DISP_INFO_MASK 0x01 +#define AVRC_PF_DISP_INFO_OFF 4 +#define AVRC_PF_DISP_INFO_SUPPORTED(x) ((x)[AVRC_PF_DISP_INFO_OFF] & AVRC_PF_DISP_INFO_MASK) + +#define AVRC_PF_HELP_BIT_NO 33 +#define AVRC_PF_HELP_MASK 0x02 +#define AVRC_PF_HELP_OFF 4 +#define AVRC_PF_HELP_SUPPORTED(x) ((x)[AVRC_PF_HELP_OFF] & AVRC_PF_HELP_MASK) + +#define AVRC_PF_PAGE_UP_BIT_NO 34 +#define AVRC_PF_PAGE_UP_MASK 0x04 +#define AVRC_PF_PAGE_UP_OFF 4 +#define AVRC_PF_PAGE_UP_SUPPORTED(x) ((x)[AVRC_PF_PAGE_UP_OFF] & AVRC_PF_PAGE_UP_MASK) + +#define AVRC_PF_PAGE_DOWN_BIT_NO 35 +#define AVRC_PF_PAGE_DOWN_MASK 0x08 +#define AVRC_PF_PAGE_DOWN_OFF 4 +#define AVRC_PF_PAGE_DOWN_SUPPORTED(x) ((x)[AVRC_PF_PAGE_DOWN_OFF] & AVRC_PF_PAGE_DOWN_MASK) + +#define AVRC_PF_POWER_BIT_NO 36 +#define AVRC_PF_POWER_MASK 0x10 +#define AVRC_PF_POWER_OFF 4 +#define AVRC_PF_POWER_SUPPORTED(x) ((x)[AVRC_PF_POWER_OFF] & AVRC_PF_POWER_MASK) + +#define AVRC_PF_VOL_UP_BIT_NO 37 +#define AVRC_PF_VOL_UP_MASK 0x20 +#define AVRC_PF_VOL_UP_OFF 4 +#define AVRC_PF_VOL_UP_SUPPORTED(x) ((x)[AVRC_PF_VOL_UP_OFF] & AVRC_PF_VOL_UP_MASK) + +#define AVRC_PF_VOL_DOWN_BIT_NO 38 +#define AVRC_PF_VOL_DOWN_MASK 0x40 +#define AVRC_PF_VOL_DOWN_OFF 4 +#define AVRC_PF_VOL_DOWN_SUPPORTED(x) ((x)[AVRC_PF_VOL_DOWN_OFF] & AVRC_PF_VOL_DOWN_MASK) + +#define AVRC_PF_MUTE_BIT_NO 39 +#define AVRC_PF_MUTE_MASK 0x80 +#define AVRC_PF_MUTE_OFF 4 +#define AVRC_PF_MUTE_SUPPORTED(x) ((x)[AVRC_PF_MUTE_OFF] & AVRC_PF_MUTE_MASK) + +#define AVRC_PF_PLAY_BIT_NO 40 +#define AVRC_PF_PLAY_MASK 0x01 +#define AVRC_PF_PLAY_OFF 5 +#define AVRC_PF_PLAY_SUPPORTED(x) ((x)[AVRC_PF_PLAY_OFF] & AVRC_PF_PLAY_MASK) + +#define AVRC_PF_STOP_BIT_NO 41 +#define AVRC_PF_STOP_MASK 0x02 +#define AVRC_PF_STOP_OFF 5 +#define AVRC_PF_STOP_SUPPORTED(x) ((x)[AVRC_PF_STOP_OFF] & AVRC_PF_STOP_MASK) + +#define AVRC_PF_PAUSE_BIT_NO 42 +#define AVRC_PF_PAUSE_MASK 0x04 +#define AVRC_PF_PAUSE_OFF 5 +#define AVRC_PF_PAUSE_SUPPORTED(x) ((x)[AVRC_PF_PAUSE_OFF] & AVRC_PF_PAUSE_MASK) + +#define AVRC_PF_RECORD_BIT_NO 43 +#define AVRC_PF_RECORD_MASK 0x08 +#define AVRC_PF_RECORD_OFF 5 +#define AVRC_PF_RECORD_SUPPORTED(x) ((x)[AVRC_PF_RECORD_OFF] & AVRC_PF_RECORD_MASK) + +#define AVRC_PF_REWIND_BIT_NO 44 +#define AVRC_PF_REWIND_MASK 0x10 +#define AVRC_PF_REWIND_OFF 5 +#define AVRC_PF_REWIND_SUPPORTED(x) ((x)[AVRC_PF_REWIND_OFF] & AVRC_PF_REWIND_MASK) + +#define AVRC_PF_FAST_FWD_BIT_NO 45 +#define AVRC_PF_FAST_FWD_MASK 0x20 +#define AVRC_PF_FAST_FWD_OFF 5 +#define AVRC_PF_FAST_FWD_SUPPORTED(x) ((x)[AVRC_PF_FAST_FWD_OFF] & AVRC_PF_FAST_FWD_MASK) + +#define AVRC_PF_EJECT_BIT_NO 46 +#define AVRC_PF_EJECT_MASK 0x40 +#define AVRC_PF_EJECT_OFF 5 +#define AVRC_PF_EJECT_SUPPORTED(x) ((x)[AVRC_PF_EJECT_OFF] & AVRC_PF_EJECT_MASK) + +#define AVRC_PF_FORWARD_BIT_NO 47 +#define AVRC_PF_FORWARD_MASK 0x80 +#define AVRC_PF_FORWARD_OFF 5 +#define AVRC_PF_FORWARD_SUPPORTED(x) ((x)[AVRC_PF_FORWARD_OFF] & AVRC_PF_FORWARD_MASK) + +#define AVRC_PF_BACKWARD_BIT_NO 48 +#define AVRC_PF_BACKWARD_MASK 0x01 +#define AVRC_PF_BACKWARD_OFF 6 +#define AVRC_PF_BACKWARD_SUPPORTED(x) ((x)[AVRC_PF_BACKWARD_OFF] & AVRC_PF_BACKWARD_MASK) + +#define AVRC_PF_ANGLE_BIT_NO 49 +#define AVRC_PF_ANGLE_MASK 0x02 +#define AVRC_PF_ANGLE_OFF 6 +#define AVRC_PF_ANGLE_SUPPORTED(x) ((x)[AVRC_PF_ANGLE_OFF] & AVRC_PF_ANGLE_MASK) + +#define AVRC_PF_SUBPICTURE_BIT_NO 50 +#define AVRC_PF_SUBPICTURE_MASK 0x04 +#define AVRC_PF_SUBPICTURE_OFF 6 +#define AVRC_PF_SUBPICTURE_SUPPORTED(x) ((x)[AVRC_PF_SUBPICTURE_OFF] & AVRC_PF_SUBPICTURE_MASK) + +#define AVRC_PF_F1_BIT_NO 51 +#define AVRC_PF_F1_MASK 0x08 +#define AVRC_PF_F1_OFF 6 +#define AVRC_PF_F1_SUPPORTED(x) ((x)[AVRC_PF_F1_OFF] & AVRC_PF_F1_MASK) + +#define AVRC_PF_F2_BIT_NO 52 +#define AVRC_PF_F2_MASK 0x10 +#define AVRC_PF_F2_OFF 6 +#define AVRC_PF_F2_SUPPORTED(x) ((x)[AVRC_PF_F2_OFF] & AVRC_PF_F2_MASK) + +#define AVRC_PF_F3_BIT_NO 53 +#define AVRC_PF_F3_MASK 0x20 +#define AVRC_PF_F3_OFF 6 +#define AVRC_PF_F3_SUPPORTED(x) ((x)[AVRC_PF_F3_OFF] & AVRC_PF_F3_MASK) + +#define AVRC_PF_F4_BIT_NO 54 +#define AVRC_PF_F4_MASK 0x40 +#define AVRC_PF_F4_OFF 6 +#define AVRC_PF_F4_SUPPORTED(x) ((x)[AVRC_PF_F4_OFF] & AVRC_PF_F4_MASK) + +#define AVRC_PF_F5_BIT_NO 55 +#define AVRC_PF_F5_MASK 0x80 +#define AVRC_PF_F5_OFF 6 +#define AVRC_PF_F5_SUPPORTED(x) ((x)[AVRC_PF_F5_OFF] & AVRC_PF_F5_MASK) + +/* Vendor unique. This PASSTHROUGH command is supported. */ +#define AVRC_PF_VENDOR_BIT_NO 56 +#define AVRC_PF_VENDOR_MASK 0x01 +#define AVRC_PF_VENDOR_OFF 7 +#define AVRC_PF_VENDOR_SUPPORTED(x) ((x)[AVRC_PF_VENDOR_OFF] & AVRC_PF_VENDOR_MASK) + +/* Basic Group Navigation. This overrules the SDP entry as it is set per player.7 */ +#define AVRC_PF_GROUP_NAVI_BIT_NO 57 +#define AVRC_PF_GROUP_NAVI_MASK 0x02 +#define AVRC_PF_GROUP_NAVI_OFF 7 +#define AVRC_PF_GROUP_NAVI_SUPPORTED(x) ((x)[AVRC_PF_GROUP_NAVI_OFF] & AVRC_PF_GROUP_NAVI_MASK) + +/* Advanced Control Player. This bit is set if the player supports at least AVRCP 1.4. */ +#define AVRC_PF_ADV_CTRL_BIT_NO 58 +#define AVRC_PF_ADV_CTRL_MASK 0x04 +#define AVRC_PF_ADV_CTRL_OFF 7 +#define AVRC_PF_ADV_CTRL_SUPPORTED(x) ((x)[AVRC_PF_ADV_CTRL_OFF] & AVRC_PF_ADV_CTRL_MASK) + +/* Browsing. This bit is set if the player supports browsing. */ +#define AVRC_PF_BROWSE_BIT_NO 59 +#define AVRC_PF_BROWSE_MASK 0x08 +#define AVRC_PF_BROWSE_OFF 7 +#define AVRC_PF_BROWSE_SUPPORTED(x) ((x)[AVRC_PF_BROWSE_OFF] & AVRC_PF_BROWSE_MASK) + +/* Searching. This bit is set if the player supports searching. */ +#define AVRC_PF_SEARCH_BIT_NO 60 +#define AVRC_PF_SEARCH_MASK 0x10 +#define AVRC_PF_SEARCH_OFF 7 +#define AVRC_PF_SEARCH_SUPPORTED(x) ((x)[AVRC_PF_SEARCH_OFF] & AVRC_PF_SEARCH_MASK) + +/* AddToNowPlaying. This bit is set if the player supports the AddToNowPlaying command. */ +#define AVRC_PF_ADD2NOWPLAY_BIT_NO 61 +#define AVRC_PF_ADD2NOWPLAY_MASK 0x20 +#define AVRC_PF_ADD2NOWPLAY_OFF 7 +#define AVRC_PF_ADD2NOWPLAY_SUPPORTED(x) ((x)[AVRC_PF_ADD2NOWPLAY_OFF] & AVRC_PF_ADD2NOWPLAY_MASK) + +/* UIDs unique in player browse tree. This bit is set if the player is able to maintain unique UIDs across the player browse tree. */ +#define AVRC_PF_UID_UNIQUE_BIT_NO 62 +#define AVRC_PF_UID_UNIQUE_MASK 0x40 +#define AVRC_PF_UID_UNIQUE_OFF 7 +#define AVRC_PF_UID_UNIQUE_SUPPORTED(x) ((x)[AVRC_PF_UID_UNIQUE_OFF] & AVRC_PF_UID_UNIQUE_MASK) + +/* OnlyBrowsableWhenAddressed. This bit is set if the player is only able to be browsed when it is set as the Addressed Player. */ +#define AVRC_PF_BR_WH_ADDR_BIT_NO 63 +#define AVRC_PF_BR_WH_ADDR_MASK 0x80 +#define AVRC_PF_BR_WH_ADDR_OFF 7 +#define AVRC_PF_BR_WH_ADDR_SUPPORTED(x) ((x)[AVRC_PF_BR_WH_ADDR_OFF] & AVRC_PF_BR_WH_ADDR_MASK) + +/* OnlySearchableWhenAddressed. This bit is set if the player is only able to be searched when it is set as the Addressed player. */ +#define AVRC_PF_SEARCH_WH_ADDR_BIT_NO 64 +#define AVRC_PF_SEARCH_WH_ADDR_MASK 0x01 +#define AVRC_PF_SEARCH_WH_ADDR_OFF 8 +#define AVRC_PF_SEARCH_WH_ADDR_SUPPORTED(x) ((x)[AVRC_PF_SEARCH_WH_ADDR_OFF] & AVRC_PF_SEARCH_WH_ADDR_MASK) + +/* NowPlaying. This bit is set if the player supports the NowPlaying folder. Note that for all players that support browsing this bit shall be set */ +#define AVRC_PF_NOW_PLAY_BIT_NO 65 +#define AVRC_PF_NOW_PLAY_MASK 0x02 +#define AVRC_PF_NOW_PLAY_OFF 8 +#define AVRC_PF_NOW_PLAY_SUPPORTED(x) ((x)[AVRC_PF_NOW_PLAY_OFF] & AVRC_PF_NOW_PLAY_MASK) + +/* UIDPersistency. This bit is set if the Player is able to persist UID values between AVRCP Browse Reconnect */ +#define AVRC_PF_UID_PERSIST_BIT_NO 66 +#define AVRC_PF_UID_PERSIST_MASK 0x04 +#define AVRC_PF_UID_PERSIST_OFF 8 +#define AVRC_PF_UID_PERSIST_SUPPORTED(x) ((x)[AVRC_PF_UID_PERSIST_OFF] & AVRC_PF_UID_PERSIST_MASK) + +/***************************************************************************** +** data type definitions +*****************************************************************************/ + +/* +This structure contains the header parameters of an AV/C message. +*/ +typedef struct +{ + UINT8 ctype; /* Command type. */ + UINT8 subunit_type; /* Subunit type. */ + UINT8 subunit_id; /* Subunit ID. This value is typically ignored in AVRCP, + * except for VENDOR DEPENDENT messages when the value is + * vendor-dependent. Value range is 0-7. */ + UINT8 opcode; /* Op Code (passthrough, vendor, etc) */ +} tAVRC_HDR; + +/* This structure contains a UNIT INFO message. */ +typedef struct +{ + tAVRC_HDR hdr; /* Message header. */ + UINT32 company_id; /* Company identifier. */ + UINT8 unit_type; /* Unit type. Uses the same values as subunit type. */ + UINT8 unit; /* This value is vendor dependent and typically zero. */ +} tAVRC_MSG_UNIT; + +/* This structure contains a SUBUNIT INFO message. */ +typedef struct +{ + tAVRC_HDR hdr; /* Message header. */ + UINT8 subunit_type[AVRC_SUB_TYPE_LEN]; + /* Array containing subunit type values. */ + BOOLEAN panel; /* TRUE if the panel subunit type is in the + * subunit_type array, FALSE otherwise. */ + UINT8 page; /* Specifies which part of the subunit type table is + * returned. For AVRCP it is typically zero. + * Value range is 0-7. */ +} tAVRC_MSG_SUB; + +/* This structure contains a VENDOR DEPENDENT message. */ +typedef struct +{ + tAVRC_HDR hdr; /* Message header. */ + UINT32 company_id; /* Company identifier. */ + UINT8 *p_vendor_data;/* Pointer to vendor dependent data. */ + UINT16 vendor_len; /* Length in bytes of vendor dependent data. */ +} tAVRC_MSG_VENDOR; + +/* PASS THROUGH message structure */ +typedef struct +{ + tAVRC_HDR hdr; /* hdr.ctype Unused. + * hdr.subunit_type Unused. + * hdr.subunit_id Unused. */ + UINT8 op_id; /* Operation ID. */ + UINT8 state; /* Keypress state. */ + UINT8 *p_pass_data;/* Pointer to data. This parameter is only valid + * when the op_id is AVRC_ID_VENDOR.*/ + UINT8 pass_len; /* Length in bytes of data. This parameter is only + * valid when the op_id is AVRC_ID_VENDOR.*/ +} tAVRC_MSG_PASS; + +/* Command/Response indicator. */ +#define AVRC_CMD AVCT_CMD /* Command message */ +#define AVRC_RSP AVCT_RSP /* Response message */ + +/* Browsing channel message structure */ +typedef struct +{ + tAVRC_HDR hdr; /* hdr.ctype AVRC_CMD or AVRC_RSP. + * hdr.subunit_type Unused. + * hdr.subunit_id Unused. */ + UINT8 *p_browse_data; /* Pointer to data. */ + UINT16 browse_len; /* Length in bytes of data. */ + BT_HDR *p_browse_pkt; /* The GKI buffer received. Set to NULL, if the callback function wants to keep the buffer */ +} tAVRC_MSG_BROWSE; + +/* This is a union of all message type structures. */ +typedef union +{ + tAVRC_HDR hdr; /* Message header. */ + tAVRC_MSG_UNIT unit; /* UNIT INFO message. */ + tAVRC_MSG_SUB sub; /* SUBUNIT INFO message. */ + tAVRC_MSG_VENDOR vendor; /* VENDOR DEPENDENT message. */ + tAVRC_MSG_PASS pass; /* PASS THROUGH message. */ + tAVRC_MSG_BROWSE browse; /* messages thru browsing channel */ +} tAVRC_MSG; + +/* macros */ +#define AVRC_IS_VALID_CAP_ID(a) (((a == AVRC_CAP_COMPANY_ID) || (a == AVRC_CAP_EVENTS_SUPPORTED)) ? TRUE : FALSE) + +#define AVRC_IS_VALID_EVENT_ID(a) (((a >= AVRC_EVT_PLAY_STATUS_CHANGE) && \ + (a <= AVRC_EVT_APP_SETTING_CHANGE)) ? TRUE : FALSE) + +#define AVRC_IS_VALID_ATTRIBUTE(a) (((((a > 0) && a <= AVRC_PLAYER_SETTING_SCAN)) || \ + ((a >= AVRC_PLAYER_SETTING_LOW_MENU_EXT) && \ + (a <= AVRC_PLAYER_SETTING_HIGH_MENU_EXT))) ? TRUE : FALSE) + + +#define AVRC_IS_VALID_MEDIA_ATTRIBUTE(a) ((a >= AVRC_MEDIA_ATTR_ID_TITLE) && \ + (a <= AVRC_MEDIA_ATTR_ID_PLAYING_TIME) ? TRUE : FALSE) + +#define AVRC_IS_VALID_BATTERY_STATUS(a) ((a <= AVRC_BATTERY_STATUS_FULL_CHARGE) ? TRUE : FALSE) + +#define AVRC_IS_VALID_SYSTEM_STATUS(a) ((a <= AVRC_SYSTEMSTATE_PWR_UNPLUGGED) ? TRUE : FALSE) + +#define AVRC_IS_VALID_GROUP(a) ((a <= AVRC_PDU_PREV_GROUP) ? TRUE : FALSE) + +/* Company ID is 24-bit integer We can not use the macros in bt_types.h */ +#define AVRC_CO_ID_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define AVRC_BE_STREAM_TO_CO_ID(u32, p) {u32 = (((UINT32)(*((p) + 2))) + (((UINT32)(*((p) + 1))) << 8) + (((UINT32)(*(p))) << 16)); (p) += 3;} + +/***************************************************************************** +** data type definitions +*****************************************************************************/ +#define AVRC_MAX_APP_ATTR_SIZE 16 +#define AVRC_MAX_CHARSET_SIZE 16 +#define AVRC_MAX_ELEM_ATTR_SIZE 8 + + +/***************************************************************************** +** Metadata transfer Building/Parsing definitions +*****************************************************************************/ + +typedef struct { + UINT16 charset_id; + UINT16 str_len; + UINT8 *p_str; +} tAVRC_FULL_NAME; + +typedef struct { + UINT16 str_len; + UINT8 *p_str; +} tAVRC_NAME; + + +#ifndef AVRC_CAP_MAX_NUM_COMP_ID +#define AVRC_CAP_MAX_NUM_COMP_ID 4 +#endif + +#ifndef AVRC_CAP_MAX_NUM_EVT_ID +#define AVRC_CAP_MAX_NUM_EVT_ID 16 +#endif + +typedef union +{ + UINT32 company_id[AVRC_CAP_MAX_NUM_COMP_ID]; + UINT8 event_id[AVRC_CAP_MAX_NUM_EVT_ID]; +} tAVRC_CAPS_PARAM; + +typedef struct +{ + UINT8 attr_id; + UINT8 attr_val; +} tAVRC_APP_SETTING; + +typedef struct +{ + UINT8 attr_id; + UINT16 charset_id; + UINT8 str_len; + UINT8 *p_str; +} tAVRC_APP_SETTING_TEXT; + +typedef UINT8 tAVRC_FEATURE_MASK[AVRC_FEATURE_MASK_SIZE]; + +typedef struct +{ + UINT16 player_id; /* A unique identifier for this media player.*/ + UINT8 major_type; /* Use AVRC_MJ_TYPE_AUDIO, AVRC_MJ_TYPE_VIDEO, AVRC_MJ_TYPE_BC_AUDIO, or AVRC_MJ_TYPE_BC_VIDEO.*/ + UINT32 sub_type; /* Use AVRC_SUB_TYPE_NONE, AVRC_SUB_TYPE_AUDIO_BOOK, or AVRC_SUB_TYPE_PODCAST*/ + UINT8 play_status; /* Use AVRC_PLAYSTATE_STOPPED, AVRC_PLAYSTATE_PLAYING, AVRC_PLAYSTATE_PAUSED, AVRC_PLAYSTATE_FWD_SEEK, + AVRC_PLAYSTATE_REV_SEEK, or AVRC_PLAYSTATE_ERROR*/ + tAVRC_FEATURE_MASK features; /* Supported feature bit mask*/ + tAVRC_FULL_NAME name; /* The player name, name length and character set id.*/ +} tAVRC_ITEM_PLAYER; + +typedef struct +{ + tAVRC_UID uid; /* The uid of this folder */ + UINT8 type; /* Use AVRC_FOLDER_TYPE_MIXED, AVRC_FOLDER_TYPE_TITLES, + AVRC_FOLDER_TYPE_ALNUMS, AVRC_FOLDER_TYPE_ARTISTS, AVRC_FOLDER_TYPE_GENRES, + AVRC_FOLDER_TYPE_PLAYLISTS, or AVRC_FOLDER_TYPE_YEARS.*/ + BOOLEAN playable; /* TRUE, if the folder can be played. */ + tAVRC_FULL_NAME name; /* The folder name, name length and character set id. */ +} tAVRC_ITEM_FOLDER; + +typedef struct +{ + UINT32 attr_id; /* Use AVRC_MEDIA_ATTR_ID_TITLE, AVRC_MEDIA_ATTR_ID_ARTIST, AVRC_MEDIA_ATTR_ID_ALBUM, + AVRC_MEDIA_ATTR_ID_TRACK_NUM, AVRC_MEDIA_ATTR_ID_NUM_TRACKS, + AVRC_MEDIA_ATTR_ID_GENRE, AVRC_MEDIA_ATTR_ID_PLAYING_TIME */ + tAVRC_FULL_NAME name; /* The attribute value, value length and character set id. */ +} tAVRC_ATTR_ENTRY; + +typedef struct +{ + tAVRC_UID uid; /* The uid of this media element item */ + UINT8 type; /* Use AVRC_MEDIA_TYPE_AUDIO or AVRC_MEDIA_TYPE_VIDEO. */ + tAVRC_FULL_NAME name; /* The media name, name length and character set id. */ + UINT8 attr_count; /* The number of attributes in p_attr_list */ + tAVRC_ATTR_ENTRY* p_attr_list; /* Attribute entry list. */ +} tAVRC_ITEM_MEDIA; + +typedef struct +{ + UINT8 item_type; /* AVRC_ITEM_PLAYER, AVRC_ITEM_FOLDER, or AVRC_ITEM_MEDIA */ + union + { + tAVRC_ITEM_PLAYER player; /* The properties of a media player item.*/ + tAVRC_ITEM_FOLDER folder; /* The properties of a folder item.*/ + tAVRC_ITEM_MEDIA media; /* The properties of a media item.*/ + } u; +} tAVRC_ITEM; + +/* GetCapability */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 capability_id; +} tAVRC_GET_CAPS_CMD; + +/* ListPlayerAppValues */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 attr_id; +} tAVRC_LIST_APP_VALUES_CMD; + +/* GetCurAppValue */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_CUR_APP_VALUE_CMD; + +/* SetAppValue */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_val; + tAVRC_APP_SETTING *p_vals; +} tAVRC_SET_APP_VALUE_CMD; + +/* GetAppAttrTxt */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_APP_ATTR_TXT_CMD; + +/* GetAppValueTxt */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 attr_id; + UINT8 num_val; + UINT8 vals[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_GET_APP_VAL_TXT_CMD; + +/* InformCharset */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_id; + UINT16 charsets[AVRC_MAX_CHARSET_SIZE]; +} tAVRC_INFORM_CHARSET_CMD; + +/* InformBatteryStatus */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 battery_status; +} tAVRC_BATTERY_STATUS_CMD; + +/* GetElemAttrs */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 num_attr; + UINT32 attrs[AVRC_MAX_ELEM_ATTR_SIZE]; +} tAVRC_GET_ELEM_ATTRS_CMD; + +/* RegNotify */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 event_id; + UINT32 param; +} tAVRC_REG_NOTIF_CMD; + +/* SetAddrPlayer */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 player_id; +} tAVRC_SET_ADDR_PLAYER_CMD; + +/* SetBrowsedPlayer */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 player_id; +} tAVRC_SET_BR_PLAYER_CMD; + +/* SetAbsVolume */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 volume; +} tAVRC_SET_VOLUME_CMD; + +/* GetFolderItems */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + UINT32 start_item; + UINT32 end_item; + UINT8 attr_count; + UINT32 *p_attr_list; +} tAVRC_GET_ITEMS_CMD; + +/* ChangePath */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT16 uid_counter; + UINT8 direction; + tAVRC_UID folder_uid; +} tAVRC_CHG_PATH_CMD; + +/* GetItemAttrs */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; + UINT8 attr_count; + UINT32 *p_attr_list; +} tAVRC_GET_ATTRS_CMD; + +/* Search */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + tAVRC_FULL_NAME string; +} tAVRC_SEARCH_CMD; + +/* PlayItem */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; +} tAVRC_PLAY_ITEM_CMD; + +/* AddToNowPlaying */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 scope; + tAVRC_UID uid; + UINT16 uid_counter; +} tAVRC_ADD_TO_PLAY_CMD; + +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ +} tAVRC_CMD; + +/* Continue and Abort */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ + UINT8 target_pdu; +} tAVRC_NEXT_CMD; + +typedef union +{ + UINT8 pdu; + tAVRC_CMD cmd; + tAVRC_GET_CAPS_CMD get_caps; /* GetCapability */ + tAVRC_CMD list_app_attr; /* ListPlayerAppAttr */ + tAVRC_LIST_APP_VALUES_CMD list_app_values; /* ListPlayerAppValues */ + tAVRC_GET_CUR_APP_VALUE_CMD get_cur_app_val; /* GetCurAppValue */ + tAVRC_SET_APP_VALUE_CMD set_app_val; /* SetAppValue */ + tAVRC_GET_APP_ATTR_TXT_CMD get_app_attr_txt; /* GetAppAttrTxt */ + tAVRC_GET_APP_VAL_TXT_CMD get_app_val_txt; /* GetAppValueTxt */ + tAVRC_INFORM_CHARSET_CMD inform_charset; /* InformCharset */ + tAVRC_BATTERY_STATUS_CMD inform_battery_status; /* InformBatteryStatus */ + tAVRC_GET_ELEM_ATTRS_CMD get_elem_attrs; /* GetElemAttrs */ + tAVRC_CMD get_play_status; /* GetPlayStatus */ + tAVRC_REG_NOTIF_CMD reg_notif; /* RegNotify */ + tAVRC_NEXT_CMD continu; /* Continue */ + tAVRC_NEXT_CMD abort; /* Abort */ + + tAVRC_SET_ADDR_PLAYER_CMD addr_player; /* SetAddrPlayer */ + tAVRC_SET_VOLUME_CMD volume; /* SetAbsVolume */ + tAVRC_SET_BR_PLAYER_CMD br_player; /* SetBrowsedPlayer */ + tAVRC_GET_ITEMS_CMD get_items; /* GetFolderItems */ + tAVRC_CHG_PATH_CMD chg_path; /* ChangePath */ + tAVRC_GET_ATTRS_CMD get_attrs; /* GetItemAttrs */ + tAVRC_SEARCH_CMD search; /* Search */ + tAVRC_PLAY_ITEM_CMD play_item; /* PlayItem */ + tAVRC_ADD_TO_PLAY_CMD add_to_play; /* AddToNowPlaying */ +} tAVRC_COMMAND; + +/* GetCapability */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 capability_id; + UINT8 count; + tAVRC_CAPS_PARAM param; +} tAVRC_GET_CAPS_RSP; + +/* ListPlayerAppAttr */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + UINT8 attrs[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_LIST_APP_ATTR_RSP; + +/* ListPlayerAppValues */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_val; + UINT8 vals[AVRC_MAX_APP_ATTR_SIZE]; +} tAVRC_LIST_APP_VALUES_RSP; + +/* GetCurAppValue */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_val; + tAVRC_APP_SETTING *p_vals; +} tAVRC_GET_CUR_APP_VALUE_RSP; + +/* GetAppAttrTxt */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + tAVRC_APP_SETTING_TEXT *p_attrs; +} tAVRC_GET_APP_ATTR_TXT_RSP; + +/* GetElemAttrs */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 num_attr; + tAVRC_ATTR_ENTRY *p_attrs; +} tAVRC_GET_ELEM_ATTRS_RSP; + +/* GetPlayStatus */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT32 song_len; + UINT32 song_pos; + UINT8 play_status; +} tAVRC_GET_PLAY_STATUS_RSP; + +/* notification event parameter for AddressedPlayer change */ +typedef struct +{ + UINT16 player_id; + UINT16 uid_counter; +} tAVRC_ADDR_PLAYER_PARAM; + +#ifndef AVRC_MAX_APP_SETTINGS +#define AVRC_MAX_APP_SETTINGS 8 +#endif + +/* notification event parameter for Player Application setting change */ +typedef struct +{ + UINT8 num_attr; + UINT8 attr_id[AVRC_MAX_APP_SETTINGS]; + UINT8 attr_value[AVRC_MAX_APP_SETTINGS]; +} tAVRC_PLAYER_APP_PARAM; + +typedef union +{ + tAVRC_PLAYSTATE play_status; + tAVRC_UID track; + UINT32 play_pos; + tAVRC_BATTERY_STATUS battery_status; + tAVRC_SYSTEMSTATE system_status; + tAVRC_PLAYER_APP_PARAM player_setting; + tAVRC_ADDR_PLAYER_PARAM addr_player; + UINT16 uid_counter; + UINT8 volume; +} tAVRC_NOTIF_RSP_PARAM; + +/* RegNotify */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 event_id; + tAVRC_NOTIF_RSP_PARAM param; +} tAVRC_REG_NOTIF_RSP; + +/* SetAbsVolume */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 volume; +} tAVRC_SET_VOLUME_RSP; + +/* SetBrowsedPlayer */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT32 num_items; + UINT16 charset_id; + UINT8 folder_depth; + tAVRC_NAME *p_folders; +} tAVRC_SET_BR_PLAYER_RSP; + +/* GetFolderItems */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT16 item_count; + tAVRC_ITEM *p_item_list; +} tAVRC_GET_ITEMS_RSP; + +/* ChangePath */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT32 num_items; +} tAVRC_CHG_PATH_RSP; + +/* GetItemAttrs */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT8 attr_count; + tAVRC_ATTR_ENTRY *p_attr_list; +} tAVRC_GET_ATTRS_RSP; + +/* Search */ +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ + UINT16 uid_counter; + UINT32 num_items; +} tAVRC_SEARCH_RSP; + + +typedef struct +{ + UINT8 pdu; + tAVRC_STS status; + UINT8 opcode; /* Op Code (copied from avrc_cmd.opcode by AVRC_BldResponse user. invalid one to generate according to pdu) */ +} tAVRC_RSP; + +typedef union +{ + UINT8 pdu; + tAVRC_RSP rsp; + tAVRC_GET_CAPS_RSP get_caps; /* GetCapability */ + tAVRC_LIST_APP_ATTR_RSP list_app_attr; /* ListPlayerAppAttr */ + tAVRC_LIST_APP_VALUES_RSP list_app_values; /* ListPlayerAppValues */ + tAVRC_GET_CUR_APP_VALUE_RSP get_cur_app_val; /* GetCurAppValue */ + tAVRC_RSP set_app_val; /* SetAppValue */ + tAVRC_GET_APP_ATTR_TXT_RSP get_app_attr_txt; /* GetAppAttrTxt */ + tAVRC_GET_APP_ATTR_TXT_RSP get_app_val_txt; /* GetAppValueTxt */ + tAVRC_RSP inform_charset; /* InformCharset */ + tAVRC_RSP inform_battery_status; /* InformBatteryStatus */ + tAVRC_GET_ELEM_ATTRS_RSP get_elem_attrs; /* GetElemAttrs */ + tAVRC_GET_PLAY_STATUS_RSP get_play_status; /* GetPlayStatus */ + tAVRC_REG_NOTIF_RSP reg_notif; /* RegNotify */ + tAVRC_RSP continu; /* Continue */ + tAVRC_RSP abort; /* Abort */ + + tAVRC_RSP addr_player; /* SetAddrPlayer */ + tAVRC_SET_VOLUME_RSP volume; /* SetAbsVolume */ + tAVRC_SET_BR_PLAYER_RSP br_player; /* SetBrowsedPlayer */ + tAVRC_GET_ITEMS_RSP get_items; /* GetFolderItems */ + tAVRC_CHG_PATH_RSP chg_path; /* ChangePath */ + tAVRC_GET_ATTRS_RSP get_attrs; /* GetItemAttrs */ + tAVRC_SEARCH_RSP search; /* Search */ + tAVRC_RSP play_item; /* PlayItem */ + tAVRC_RSP add_to_play; /* AddToNowPlaying */ +} tAVRC_RESPONSE; + + +#endif diff --git a/stack/include/bnep_api.h b/stack/include/bnep_api.h new file mode 100644 index 0000000..b2bc0fd --- /dev/null +++ b/stack/include/bnep_api.h @@ -0,0 +1,476 @@ +/****************************************************************************** + * + * Copyright (C) 2001-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 interface file contains the interface to the Bluetooth Network + * Encapsilation Protocol (BNEP). + * + ******************************************************************************/ +#ifndef BNEP_API_H +#define BNEP_API_H + +#include "l2c_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Define the minimum offset needed in a GKI buffer for +** sending BNEP packets. Note, we are currently not sending +** extension headers, but may in the future, so allow +** space for them +*/ +#define BNEP_MINIMUM_OFFSET (15 + L2CAP_MIN_OFFSET) +#define BNEP_INVALID_HANDLE 0xFFFF + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Define the result codes from BNEP +*/ +enum +{ + BNEP_SUCCESS, /* Success */ + BNEP_CONN_DISCONNECTED, /* Connection terminated */ + BNEP_NO_RESOURCES, /* No resources */ + BNEP_MTU_EXCEDED, /* Attempt to write long data */ + BNEP_INVALID_OFFSET, /* Insufficient offset in GKI buffer */ + BNEP_CONN_FAILED, /* Connection failed */ + BNEP_CONN_FAILED_CFG, /* Connection failed cos of config */ + BNEP_CONN_FAILED_SRC_UUID, /* Connection failed wrong source UUID */ + BNEP_CONN_FAILED_DST_UUID, /* Connection failed wrong destination UUID */ + BNEP_CONN_FAILED_UUID_SIZE, /* Connection failed wrong size UUID */ + BNEP_Q_SIZE_EXCEEDED, /* Too many buffers to dest */ + BNEP_TOO_MANY_FILTERS, /* Too many local filters specified */ + BNEP_SET_FILTER_FAIL, /* Set Filter failed */ + BNEP_WRONG_HANDLE, /* Wrong handle for the connection */ + BNEP_WRONG_STATE, /* Connection is in wrong state */ + BNEP_SECURITY_FAIL, /* Failed because of security */ + BNEP_IGNORE_CMD, /* To ignore the rcvd command */ + BNEP_TX_FLOW_ON, /* tx data flow enabled */ + BNEP_TX_FLOW_OFF /* tx data flow disabled */ + +}; typedef UINT8 tBNEP_RESULT; + + +/*************************** +** Callback Functions +****************************/ + +/* Connection state change callback prototype. Parameters are +** Connection handle +** BD Address of remote +** Connection state change result +** BNEP_SUCCESS indicates connection is success +** All values are used to indicate the reason for failure +** Flag to indicate if it is just a role change +*/ +typedef void (tBNEP_CONN_STATE_CB) (UINT16 handle, + BD_ADDR rem_bda, + tBNEP_RESULT result, + BOOLEAN is_role_change); + + + + +/* Connection indication callback prototype. Parameters are +** BD Address of remote, remote UUID and local UUID +** and flag to indicate role change and handle to the connection +** When BNEP calls this function profile should +** use BNEP_ConnectResp call to accept or reject the request +*/ +typedef void (tBNEP_CONNECT_IND_CB) (UINT16 handle, + BD_ADDR bd_addr, + tBT_UUID *remote_uuid, + tBT_UUID *local_uuid, + BOOLEAN is_role_change); + + + +/* Data buffer received indication callback prototype. Parameters are +** Handle to the connection +** Source BD/Ethernet Address +** Dest BD/Ethernet address +** Protocol +** Pointer to the buffer +** Flag to indicate whether extension headers to be forwarded are present +*/ +typedef void (tBNEP_DATA_BUF_CB) (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + BT_HDR *p_buf, + BOOLEAN fw_ext_present); + + +/* Data received indication callback prototype. Parameters are +** Handle to the connection +** Source BD/Ethernet Address +** Dest BD/Ethernet address +** Protocol +** Pointer to the beginning of the data +** Length of data +** Flag to indicate whether extension headers to be forwarded are present +*/ +typedef void (tBNEP_DATA_IND_CB) (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + UINT8 *p_data, + UINT16 len, + BOOLEAN fw_ext_present); + +/* Flow control callback for TX data. Parameters are +** Handle to the connection +** Event flow status +*/ +typedef void (tBNEP_TX_DATA_FLOW_CB) (UINT16 handle, + tBNEP_RESULT event); + +/* Filters received indication callback prototype. Parameters are +** Handle to the connection +** TRUE if the cb is called for indication +** Ignore this if it is indication, otherwise it is the result +** for the filter set operation performed by the local +** device +** Number of protocol filters present +** Pointer to the filters start. Filters are present in pairs +** of start of the range and end of the range. +** They will be present in big endian order. First +** two bytes will be starting of the first range and +** next two bytes will be ending of the range. +*/ +typedef void (tBNEP_FILTER_IND_CB) (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters); + + + +/* Multicast Filters received indication callback prototype. Parameters are +** Handle to the connection +** TRUE if the cb is called for indication +** Ignore this if it is indication, otherwise it is the result +** for the filter set operation performed by the local +** device +** Number of multicast filters present +** Pointer to the filters start. Filters are present in pairs +** of start of the range and end of the range. +** First six bytes will be starting of the first range and +** next six bytes will be ending of the range. +*/ +typedef void (tBNEP_MFILTER_IND_CB) (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_mfilters, + UINT8 *p_mfilters); + +/* This is the structure used by profile to register with BNEP */ +typedef struct +{ + tBNEP_CONNECT_IND_CB *p_conn_ind_cb; /* To indicate the conn request */ + tBNEP_CONN_STATE_CB *p_conn_state_cb; /* To indicate conn state change */ + tBNEP_DATA_IND_CB *p_data_ind_cb; /* To pass the data received */ + tBNEP_DATA_BUF_CB *p_data_buf_cb; /* To pass the data buffer received */ + tBNEP_TX_DATA_FLOW_CB *p_tx_data_flow_cb; /* data flow callback */ + tBNEP_FILTER_IND_CB *p_filter_ind_cb; /* To indicate that peer set protocol filters */ + tBNEP_MFILTER_IND_CB *p_mfilter_ind_cb; /* To indicate that peer set mcast filters */ + +} tBNEP_REGISTER; + + + +/* This is the structure used by profile to get the status of BNEP */ +typedef struct +{ +#define BNEP_STATUS_FAILE 0 +#define BNEP_STATUS_CONNECTED 1 + UINT8 con_status; + + UINT16 l2cap_cid; + BD_ADDR rem_bda; + UINT16 rem_mtu_size; + UINT16 xmit_q_depth; + + UINT16 sent_num_filters; + UINT16 sent_mcast_filters; + UINT16 rcvd_num_filters; + UINT16 rcvd_mcast_filters; + tBT_UUID src_uuid; + tBT_UUID dst_uuid; + +} tBNEP_STATUS; + + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* +** +** Function BNEP_Register +** +** Description This function is called by the upper layer to register +** its callbacks with BNEP +** +** Parameters: p_reg_info - contains all callback function pointers +** +** +** Returns BNEP_SUCCESS if registered successfully +** BNEP_FAILURE if connection state callback is missing +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_Register (tBNEP_REGISTER *p_reg_info); + +/******************************************************************************* +** +** Function BNEP_Deregister +** +** Description This function is called by the upper layer to de-register +** its callbacks. +** +** Parameters: void +** +** +** Returns void +** +*******************************************************************************/ +BNEP_API extern void BNEP_Deregister (void); + + +/******************************************************************************* +** +** Function BNEP_Connect +** +** Description This function creates a BNEP connection to a remote +** device. +** +** Parameters: p_rem_addr - BD_ADDR of the peer +** src_uuid - source uuid for the connection +** dst_uuid - destination uuid for the connection +** p_handle - pointer to return the handle for the connection +** +** Returns BNEP_SUCCESS if connection started +** BNEP_NO_RESOURCES if no resources +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_Connect (BD_ADDR p_rem_bda, + tBT_UUID *src_uuid, + tBT_UUID *dst_uuid, + UINT16 *p_handle); + +/******************************************************************************* +** +** Function BNEP_ConnectResp +** +** Description This function is called in responce to connection indication +** +** +** Parameters: handle - handle given in the connection indication +** resp - responce for the connection indication +** +** Returns BNEP_SUCCESS if connection started +** BNEP_WRONG_HANDLE if the connection is not found +** BNEP_WRONG_STATE if the responce is not expected +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_ConnectResp (UINT16 handle, tBNEP_RESULT resp); + +/******************************************************************************* +** +** Function BNEP_Disconnect +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - handle of the connection +** +** Returns BNEP_SUCCESS if connection is disconnected +** BNEP_WRONG_HANDLE if no connection is not found +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_Disconnect (UINT16 handle); + +/******************************************************************************* +** +** Function BNEP_WriteBuf +** +** Description This function sends data in a GKI buffer on BNEP connection +** +** Parameters: handle - handle of the connection to write +** p_dest_addr - BD_ADDR/Ethernet addr of the destination +** p_buf - pointer to address of buffer with data +** protocol - protocol type of the packet +** p_src_addr - (optional) BD_ADDR/ethernet address of the source +** (should be NULL if it is local BD Addr) +** fw_ext_present - forwarded extensions present +** +** Returns: BNEP_WRONG_HANDLE - if passed handle is not valid +** BNEP_MTU_EXCEDED - If the data length is greater than MTU +** BNEP_IGNORE_CMD - If the packet is filtered out +** BNEP_Q_SIZE_EXCEEDED - If the Tx Q is full +** BNEP_SUCCESS - If written successfully +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_WriteBuf (UINT16 handle, + UINT8 *p_dest_addr, + BT_HDR *p_buf, + UINT16 protocol, + UINT8 *p_src_addr, + BOOLEAN fw_ext_present); + +/******************************************************************************* +** +** Function BNEP_Write +** +** Description This function sends data over a BNEP connection +** +** Parameters: handle - handle of the connection to write +** p_dest_addr - BD_ADDR/Ethernet addr of the destination +** p_data - pointer to data start +** protocol - protocol type of the packet +** p_src_addr - (optional) BD_ADDR/ethernet address of the source +** (should be NULL if it is local BD Addr) +** fw_ext_present - forwarded extensions present +** +** Returns: BNEP_WRONG_HANDLE - if passed handle is not valid +** BNEP_MTU_EXCEDED - If the data length is greater than MTU +** BNEP_IGNORE_CMD - If the packet is filtered out +** BNEP_Q_SIZE_EXCEEDED - If the Tx Q is full +** BNEP_NO_RESOURCES - If not able to allocate a buffer +** BNEP_SUCCESS - If written successfully +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_Write (UINT16 handle, + UINT8 *p_dest_addr, + UINT8 *p_data, + UINT16 len, + UINT16 protocol, + UINT8 *p_src_addr, + BOOLEAN fw_ext_present); + +/******************************************************************************* +** +** Function BNEP_SetProtocolFilters +** +** Description This function sets the protocol filters on peer device +** +** Parameters: handle - Handle for the connection +** num_filters - total number of filter ranges +** p_start_array - Array of beginings of all protocol ranges +** p_end_array - Array of ends of all protocol ranges +** +** Returns BNEP_WRONG_HANDLE - if the connection handle is not valid +** BNEP_SET_FILTER_FAIL - if the connection is in wrong state +** BNEP_TOO_MANY_FILTERS - if too many filters +** BNEP_SUCCESS - if request sent successfully +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_SetProtocolFilters (UINT16 handle, + UINT16 num_filters, + UINT16 *p_start_array, + UINT16 *p_end_array); + +/******************************************************************************* +** +** Function BNEP_SetMulticastFilters +** +** Description This function sets the filters for multicast addresses for BNEP. +** +** Parameters: handle - Handle for the connection +** num_filters - total number of filter ranges +** p_start_array - Pointer to sequence of beginings of all +** multicast address ranges +** p_end_array - Pointer to sequence of ends of all +** multicast address ranges +** +** Returns BNEP_WRONG_HANDLE - if the connection handle is not valid +** BNEP_SET_FILTER_FAIL - if the connection is in wrong state +** BNEP_TOO_MANY_FILTERS - if too many filters +** BNEP_SUCCESS - if request sent successfully +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_SetMulticastFilters (UINT16 handle, + UINT16 num_filters, + UINT8 *p_start_array, + UINT8 *p_end_array); + +/******************************************************************************* +** +** Function BNEP_GetMyBdAddr +** +** Description This function returns a pointer to the local device BD address. +** If the BD address has not been read yet, it returns NULL. +** +** Returns the BD address +** +*******************************************************************************/ +BNEP_API extern UINT8 *BNEP_GetMyBdAddr (void); + +/******************************************************************************* +** +** Function BNEP_SetTraceLevel +** +** Description This function sets the trace level for BNEP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +BNEP_API extern UINT8 BNEP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function BNEP_Init +** +** Description This function initializes the BNEP unit. It should be called +** before accessing any other APIs to initialize the control block +** +** Returns void +** +*******************************************************************************/ +BNEP_API extern void BNEP_Init (void); + +/******************************************************************************* +** +** Function BNEP_GetStatus +** +** Description This function gets the status information for BNEP connection +** +** Returns BNEP_SUCCESS - if the status is available +** BNEP_NO_RESOURCES - if no structure is passed for output +** BNEP_WRONG_HANDLE - if the handle is invalid +** BNEP_WRONG_STATE - if not in connected state +** +*******************************************************************************/ +BNEP_API extern tBNEP_RESULT BNEP_GetStatus (UINT16 handle, tBNEP_STATUS *p_status); + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h new file mode 100644 index 0000000..5809968 --- /dev/null +++ b/stack/include/bt_types.h @@ -0,0 +1,690 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BT_TYPES_H +#define BT_TYPES_H + +#include "data_types.h" + +#ifdef _WIN32 +#ifdef BLUESTACK_TESTER + #include "bte_stack_entry.h" +#endif +#endif + +/* READ WELL !! +** +** This section defines global events. These are events that cross layers. +** Any event that passes between layers MUST be one of these events. Tasks +** can use their own events internally, but a FUNDAMENTAL design issue is +** that global events MUST be one of these events defined below. +** +** The convention used is the the event name contains the layer that the +** event is going to. +*/ +#define BT_EVT_MASK 0xFF00 +#define BT_SUB_EVT_MASK 0x00FF + /* To Bluetooth Upper Layers */ + /************************************/ +#define BT_EVT_TO_BTU_L2C_EVT 0x0900 /* L2CAP event */ +#define BT_EVT_TO_BTU_HCI_EVT 0x1000 /* HCI Event */ +#define BT_EVT_TO_BTU_HCI_BR_EDR_EVT (0x0000 | BT_EVT_TO_BTU_HCI_EVT) /* event from BR/EDR controller */ +#define BT_EVT_TO_BTU_HCI_AMP1_EVT (0x0001 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 1 controller */ +#define BT_EVT_TO_BTU_HCI_AMP2_EVT (0x0002 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 2 controller */ +#define BT_EVT_TO_BTU_HCI_AMP3_EVT (0x0003 | BT_EVT_TO_BTU_HCI_EVT) /* event from local AMP 3 controller */ + +#define BT_EVT_TO_BTU_HCI_ACL 0x1100 /* ACL Data from HCI */ +#define BT_EVT_TO_BTU_HCI_SCO 0x1200 /* SCO Data from HCI */ +#define BT_EVT_TO_BTU_HCIT_ERR 0x1300 /* HCI Transport Error */ + +#define BT_EVT_TO_BTU_SP_EVT 0x1400 /* Serial Port Event */ +#define BT_EVT_TO_BTU_SP_DATA 0x1500 /* Serial Port Data */ + +#define BT_EVT_TO_BTU_HCI_CMD 0x1600 /* HCI command from upper layer */ + + +#define BT_EVT_TO_BTU_L2C_SEG_XMIT 0x1900 /* L2CAP segment(s) transmitted */ + +#define BT_EVT_PROXY_INCOMING_MSG 0x1A00 /* BlueStackTester event: incoming message from target */ + +#define BT_EVT_BTSIM 0x1B00 /* Insight BTSIM event */ +#define BT_EVT_BTISE 0x1C00 /* Insight Script Engine event */ + + /* To LM */ + /************************************/ +#define BT_EVT_TO_LM_HCI_CMD 0x2000 /* HCI Command */ +#define BT_EVT_TO_LM_HCI_ACL 0x2100 /* HCI ACL Data */ +#define BT_EVT_TO_LM_HCI_SCO 0x2200 /* HCI SCO Data */ +#define BT_EVT_TO_LM_HCIT_ERR 0x2300 /* HCI Transport Error */ +#define BT_EVT_TO_LM_LC_EVT 0x2400 /* LC event */ +#define BT_EVT_TO_LM_LC_LMP 0x2500 /* LC Received LMP command frame */ +#define BT_EVT_TO_LM_LC_ACL 0x2600 /* LC Received ACL data */ +#define BT_EVT_TO_LM_LC_SCO 0x2700 /* LC Received SCO data (not used) */ +#define BT_EVT_TO_LM_LC_ACL_TX 0x2800 /* LMP data transmit complete */ +#define BT_EVT_TO_LM_LC_LMPC_TX 0x2900 /* LMP Command transmit complete */ +#define BT_EVT_TO_LM_LOCAL_ACL_LB 0x2a00 /* Data to be locally loopbacked */ +#define BT_EVT_TO_LM_HCI_ACL_ACK 0x2b00 /* HCI ACL Data ack (not used) */ +#define BT_EVT_TO_LM_DIAG 0x2c00 /* LM Diagnostics commands */ + + +#define BT_EVT_TO_BTM_CMDS 0x2f00 +#define BT_EVT_TO_BTM_PM_MDCHG_EVT (0x0001 | BT_EVT_TO_BTM_CMDS) + +#define BT_EVT_TO_TCS_CMDS 0x3000 + +#define BT_EVT_TO_OBX_CL_MSG 0x3100 +#define BT_EVT_TO_OBX_SR_MSG 0x3200 + +#define BT_EVT_TO_CTP_CMDS 0x3300 + +/* Obex Over L2CAP */ +#define BT_EVT_TO_OBX_CL_L2C_MSG 0x3400 +#define BT_EVT_TO_OBX_SR_L2C_MSG 0x3500 + +/* ftp events */ +#define BT_EVT_TO_FTP_SRVR_CMDS 0x3800 +#define BT_EVT_TO_FTP_CLNT_CMDS 0x3900 + +#define BT_EVT_TO_BTU_SAP 0x3a00 /* SIM Access Profile events */ + +/* opp events */ +#define BT_EVT_TO_OPP_SRVR_CMDS 0x3b00 +#define BT_EVT_TO_OPP_CLNT_CMDS 0x3c00 + +/* gap events */ +#define BT_EVT_TO_GAP_MSG 0x3d00 + +/* start timer */ +#define BT_EVT_TO_START_TIMER 0x3e00 + +/* start quick timer */ +#define BT_EVT_TO_START_QUICK_TIMER 0x3f00 + + +/* for NFC */ + /************************************/ +#define BT_EVT_TO_NFC_NCI 0x4000 /* NCI Command, Notification or Data*/ +#define BT_EVT_TO_NFC_INIT 0x4100 /* Initialization message */ +#define BT_EVT_TO_LLCP_ECHO 0x4200 /* LLCP Echo Service */ +#define BT_EVT_TO_LLCP_SOCKET 0x4300 /* LLCP over TCP/IP */ +#define BT_EVT_TO_NCI_LP 0x4400 /* Low power */ +#define BT_EVT_TO_NFC_ERR 0x4500 /* Error notification to NFC Task */ + +#define BT_EVT_TO_NFCCSIM_NCI 0x4a00 /* events to NFCC simulation (NCI packets) */ + +/* HCISU Events */ + +#define BT_EVT_HCISU 0x5000 + +// btla-specific ++ +#define BT_EVT_TO_HCISU_RECONFIG_EVT (0x0001 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_UPDATE_BAUDRATE_EVT (0x0002 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_ENABLE_EVT (0x0003 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_DISABLE_EVT (0x0004 | BT_EVT_HCISU) +// btla-specific -- +#define BT_EVT_TO_HCISU_LP_APP_SLEEPING_EVT (0x0005 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_ALLOW_BT_SLEEP_EVT (0x0006 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_WAKEUP_HOST_EVT (0x0007 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_LP_RCV_H4IBSS_EVT (0x0008 | BT_EVT_HCISU) +#define BT_EVT_TO_HCISU_H5_RESET_EVT (0x0009 | BT_EVT_HCISU) +#define BT_EVT_HCISU_START_QUICK_TIMER (0x000a | BT_EVT_HCISU) + +#define BT_EVT_DATA_TO_AMP_1 0x5100 +#define BT_EVT_DATA_TO_AMP_15 0x5f00 + +/* HSP Events */ + +#define BT_EVT_BTU_HSP2 0x6000 + +#define BT_EVT_TO_BTU_HSP2_EVT (0x0001 | BT_EVT_BTU_HSP2) + +/* BPP Events */ +#define BT_EVT_TO_BPP_PR_CMDS 0x6100 /* Printer Events */ +#define BT_EVT_TO_BPP_SND_CMDS 0x6200 /* BPP Sender Events */ + +/* BIP Events */ +#define BT_EVT_TO_BIP_CMDS 0x6300 + +/* HCRP Events */ + +#define BT_EVT_BTU_HCRP 0x7000 + +#define BT_EVT_TO_BTU_HCRP_EVT (0x0001 | BT_EVT_BTU_HCRP) +#define BT_EVT_TO_BTU_HCRPM_EVT (0x0002 | BT_EVT_BTU_HCRP) + + +#define BT_EVT_BTU_HFP 0x8000 +#define BT_EVT_TO_BTU_HFP_EVT (0x0001 | BT_EVT_BTU_HFP) + +#define BT_EVT_BTU_IPC_EVT 0x9000 +#define BT_EVT_BTU_IPC_LOGMSG_EVT (0x0000 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_ACL_EVT (0x0001 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTU_EVT (0x0002 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_EVT (0x0003 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_MSG_EVT (0x0004 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTM_EVT (0x0005 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_AVDT_EVT (0x0006 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_SLIP_EVT (0x0007 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_MGMT_EVT (0x0008 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTTRC_EVT (0x0009 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BURST_EVT (0x000A | BT_EVT_BTU_IPC_EVT) + + +/* BTIF Events */ +#define BT_EVT_BTIF 0xA000 +#define BT_EVT_CONTEXT_SWITCH_EVT (0x0001 | BT_EVT_BTIF) + +#define BT_EVT_TRIGGER_STACK_INIT EVENT_MASK(APPL_EVT_0) + + +/* Define the header of each buffer used in the Bluetooth stack. +*/ +typedef struct +{ + UINT16 event; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} BT_HDR; + +#define BT_HDR_SIZE (sizeof (BT_HDR)) + +#define BT_PSM_SDP 0x0001 +#define BT_PSM_RFCOMM 0x0003 +#define BT_PSM_TCS 0x0005 +#define BT_PSM_CTP 0x0007 +#define BT_PSM_BNEP 0x000F +#define BT_PSM_HIDC 0x0011 +#define BT_PSM_HIDI 0x0013 +#define BT_PSM_UPNP 0x0015 +#define BT_PSM_AVCTP 0x0017 +#define BT_PSM_AVDTP 0x0019 +#define BT_PSM_AVCTP_13 0x001B /* Advanced Control - Browsing */ +#define BT_PSM_UDI_CP 0x001D /* Unrestricted Digital Information Profile C-Plane */ +#define BT_PSM_ATT 0x001F /* Attribute Protocol */ + + +/* These macros extract the HCI opcodes from a buffer +*/ +#define HCI_GET_CMD_HDR_OPCODE(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset) + \ + (*((UINT8 *)((p) + 1) + p->offset + 1) << 8))) +#define HCI_GET_CMD_HDR_PARAM_LEN(p) (UINT8) (*((UINT8 *)((p) + 1) + p->offset + 2)) + +#define HCI_GET_EVT_HDR_OPCODE(p) (UINT8)(*((UINT8 *)((p) + 1) + p->offset)) +#define HCI_GET_EVT_HDR_PARAM_LEN(p) (UINT8) (*((UINT8 *)((p) + 1) + p->offset + 1)) + + +/******************************************************************************** +** Macros to get and put bytes to and from a stream (Little Endian format). +*/ +#define UINT32_TO_STREAM(p, u32) {*(p)++ = (UINT8)(u32); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 24);} +#define UINT24_TO_STREAM(p, u24) {*(p)++ = (UINT8)(u24); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)((u24) >> 16);} +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);} +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} +#define INT8_TO_STREAM(p, u8) {*(p)++ = (INT8)(u8);} +#define ARRAY32_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 32; ijk++) *(p)++ = (UINT8) a[31 - ijk];} +#define ARRAY16_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 16; ijk++) *(p)++ = (UINT8) a[15 - ijk];} +#define ARRAY8_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < 8; ijk++) *(p)++ = (UINT8) a[7 - ijk];} +#define BDADDR_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *(p)++ = (UINT8) a[BD_ADDR_LEN - 1 - ijk];} +#define LAP_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < LAP_LEN; ijk++) *(p)++ = (UINT8) a[LAP_LEN - 1 - ijk];} +#define DEVCLASS_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < DEV_CLASS_LEN;ijk++) *(p)++ = (UINT8) a[DEV_CLASS_LEN - 1 - ijk];} +#define ARRAY_TO_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[ijk];} +#define REVERSE_ARRAY_TO_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[len - 1 - ijk];} + +#define STREAM_TO_UINT8(u8, p) {u8 = (UINT8)(*(p)); (p) += 1;} +#define STREAM_TO_UINT16(u16, p) {u16 = ((UINT16)(*(p)) + (((UINT16)(*((p) + 1))) << 8)); (p) += 2;} +#define STREAM_TO_UINT24(u32, p) {u32 = (((UINT32)(*(p))) + ((((UINT32)(*((p) + 1)))) << 8) + ((((UINT32)(*((p) + 2)))) << 16) ); (p) += 3;} +#define STREAM_TO_UINT32(u32, p) {u32 = (((UINT32)(*(p))) + ((((UINT32)(*((p) + 1)))) << 8) + ((((UINT32)(*((p) + 2)))) << 16) + ((((UINT32)(*((p) + 3)))) << 24)); (p) += 4;} +#define STREAM_TO_BDADDR(a, p) {register int ijk; register UINT8 *pbda = (UINT8 *)a + BD_ADDR_LEN - 1; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *pbda-- = *p++;} +#define STREAM_TO_ARRAY32(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 31; for (ijk = 0; ijk < 32; ijk++) *_pa-- = *p++;} +#define STREAM_TO_ARRAY16(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 15; for (ijk = 0; ijk < 16; ijk++) *_pa-- = *p++;} +#define STREAM_TO_ARRAY8(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + 7; for (ijk = 0; ijk < 8; ijk++) *_pa-- = *p++;} +#define STREAM_TO_DEVCLASS(a, p) {register int ijk; register UINT8 *_pa = (UINT8 *)a + DEV_CLASS_LEN - 1; for (ijk = 0; ijk < DEV_CLASS_LEN; ijk++) *_pa-- = *p++;} +#define STREAM_TO_LAP(a, p) {register int ijk; register UINT8 *plap = (UINT8 *)a + LAP_LEN - 1; for (ijk = 0; ijk < LAP_LEN; ijk++) *plap-- = *p++;} +#define STREAM_TO_ARRAY(a, p, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} +#define REVERSE_STREAM_TO_ARRAY(a, p, len) {register int ijk; register UINT8 *_pa = (UINT8 *)a + len - 1; for (ijk = 0; ijk < len; ijk++) *_pa-- = *p++;} + +/******************************************************************************** +** Macros to get and put bytes to and from a field (Little Endian format). +** These are the same as to stream, except the pointer is not incremented. +*/ +#define UINT32_TO_FIELD(p, u32) {*(UINT8 *)(p) = (UINT8)(u32); *((UINT8 *)(p)+1) = (UINT8)((u32) >> 8); *((UINT8 *)(p)+2) = (UINT8)((u32) >> 16); *((UINT8 *)(p)+3) = (UINT8)((u32) >> 24);} +#define UINT24_TO_FIELD(p, u24) {*(UINT8 *)(p) = (UINT8)(u24); *((UINT8 *)(p)+1) = (UINT8)((u24) >> 8); *((UINT8 *)(p)+2) = (UINT8)((u24) >> 16);} +#define UINT16_TO_FIELD(p, u16) {*(UINT8 *)(p) = (UINT8)(u16); *((UINT8 *)(p)+1) = (UINT8)((u16) >> 8);} +#define UINT8_TO_FIELD(p, u8) {*(UINT8 *)(p) = (UINT8)(u8);} + + +/******************************************************************************** +** Macros to get and put bytes to and from a stream (Big Endian format) +*/ +#define UINT32_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 24); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define UINT24_TO_BE_STREAM(p, u24) {*(p)++ = (UINT8)((u24) >> 16); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)(u24);} +#define UINT16_TO_BE_STREAM(p, u16) {*(p)++ = (UINT8)((u16) >> 8); *(p)++ = (UINT8)(u16);} +#define UINT8_TO_BE_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} +#define ARRAY_TO_BE_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[ijk];} + +#define BE_STREAM_TO_UINT8(u8, p) {u8 = (UINT8)(*(p)); (p) += 1;} +#define BE_STREAM_TO_UINT16(u16, p) {u16 = (UINT16)(((UINT16)(*(p)) << 8) + (UINT16)(*((p) + 1))); (p) += 2;} +#define BE_STREAM_TO_UINT24(u32, p) {u32 = (((UINT32)(*((p) + 2))) + ((UINT32)(*((p) + 1)) << 8) + ((UINT32)(*(p)) << 16)); (p) += 3;} +#define BE_STREAM_TO_UINT32(u32, p) {u32 = ((UINT32)(*((p) + 3)) + ((UINT32)(*((p) + 2)) << 8) + ((UINT32)(*((p) + 1)) << 16) + ((UINT32)(*(p)) << 24)); (p) += 4;} +#define BE_STREAM_TO_ARRAY(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} + + +/******************************************************************************** +** Macros to get and put bytes to and from a field (Big Endian format). +** These are the same as to stream, except the pointer is not incremented. +*/ +#define UINT32_TO_BE_FIELD(p, u32) {*(UINT8 *)(p) = (UINT8)((u32) >> 24); *((UINT8 *)(p)+1) = (UINT8)((u32) >> 16); *((UINT8 *)(p)+2) = (UINT8)((u32) >> 8); *((UINT8 *)(p)+3) = (UINT8)(u32); } +#define UINT24_TO_BE_FIELD(p, u24) {*(UINT8 *)(p) = (UINT8)((u24) >> 16); *((UINT8 *)(p)+1) = (UINT8)((u24) >> 8); *((UINT8 *)(p)+2) = (UINT8)(u24);} +#define UINT16_TO_BE_FIELD(p, u16) {*(UINT8 *)(p) = (UINT8)((u16) >> 8); *((UINT8 *)(p)+1) = (UINT8)(u16);} +#define UINT8_TO_BE_FIELD(p, u8) {*(UINT8 *)(p) = (UINT8)(u8);} + + +/* Common Bluetooth field definitions */ +#define BD_ADDR_LEN 6 /* Device address length */ +typedef UINT8 BD_ADDR[BD_ADDR_LEN]; /* Device address */ +typedef UINT8 *BD_ADDR_PTR; /* Pointer to Device Address */ + +#define AMP_KEY_TYPE_GAMP 0 +#define AMP_KEY_TYPE_WIFI 1 +#define AMP_KEY_TYPE_UWB 2 +typedef UINT8 tAMP_KEY_TYPE; + +#define BT_OCTET8_LEN 8 +typedef UINT8 BT_OCTET8[BT_OCTET8_LEN]; /* octet array: size 16 */ + +#define LINK_KEY_LEN 16 +typedef UINT8 LINK_KEY[LINK_KEY_LEN]; /* Link Key */ + +#define AMP_LINK_KEY_LEN 32 +typedef UINT8 AMP_LINK_KEY[AMP_LINK_KEY_LEN]; /* Dedicated AMP and GAMP Link Keys */ + +#define BT_OCTET16_LEN 16 +typedef UINT8 BT_OCTET16[BT_OCTET16_LEN]; /* octet array: size 16 */ + +#define PIN_CODE_LEN 16 +typedef UINT8 PIN_CODE[PIN_CODE_LEN]; /* Pin Code (upto 128 bits) MSB is 0 */ +typedef UINT8 *PIN_CODE_PTR; /* Pointer to Pin Code */ + +#define DEV_CLASS_LEN 3 +typedef UINT8 DEV_CLASS[DEV_CLASS_LEN]; /* Device class */ +typedef UINT8 *DEV_CLASS_PTR; /* Pointer to Device class */ + +#define EXT_INQ_RESP_LEN 3 +typedef UINT8 EXT_INQ_RESP[EXT_INQ_RESP_LEN];/* Extended Inquiry Response */ +typedef UINT8 *EXT_INQ_RESP_PTR; /* Pointer to Extended Inquiry Response */ + +#define BD_NAME_LEN 248 +typedef UINT8 BD_NAME[BD_NAME_LEN]; /* Device name */ +typedef UINT8 *BD_NAME_PTR; /* Pointer to Device name */ + +#define BD_FEATURES_LEN 8 +typedef UINT8 BD_FEATURES[BD_FEATURES_LEN]; /* LMP features supported by device */ + +#define BT_EVENT_MASK_LEN 8 +typedef UINT8 BT_EVENT_MASK[BT_EVENT_MASK_LEN]; /* Event Mask */ + +#define LAP_LEN 3 +typedef UINT8 LAP[LAP_LEN]; /* IAC as passed to Inquiry (LAP) */ +typedef UINT8 INQ_LAP[LAP_LEN]; /* IAC as passed to Inquiry (LAP) */ + +#define RAND_NUM_LEN 16 +typedef UINT8 RAND_NUM[RAND_NUM_LEN]; + +#define ACO_LEN 12 +typedef UINT8 ACO[ACO_LEN]; /* Authenticated ciphering offset */ + +#define COF_LEN 12 +typedef UINT8 COF[COF_LEN]; /* ciphering offset number */ + +typedef struct { + UINT8 qos_flags; /* TBD */ + UINT8 service_type; /* see below */ + UINT32 token_rate; /* bytes/second */ + UINT32 token_bucket_size; /* bytes */ + UINT32 peak_bandwidth; /* bytes/second */ + UINT32 latency; /* microseconds */ + UINT32 delay_variation; /* microseconds */ +} FLOW_SPEC; + +/* Values for service_type */ +#define NO_TRAFFIC 0 +#define BEST_EFFORT 1 +#define GUARANTEED 2 + +/* Service class of the CoD */ +#define SERV_CLASS_NETWORKING (1 << 1) +#define SERV_CLASS_RENDERING (1 << 2) +#define SERV_CLASS_CAPTURING (1 << 3) +#define SERV_CLASS_OBJECT_TRANSFER (1 << 4) +#define SERV_CLASS_OBJECT_AUDIO (1 << 5) +#define SERV_CLASS_OBJECT_TELEPHONY (1 << 6) +#define SERV_CLASS_OBJECT_INFORMATION (1 << 7) + +/* Second byte */ +#define SERV_CLASS_LIMITED_DISC_MODE (0x20) + +/* Field size definitions. Note that byte lengths are rounded up. */ +#define ACCESS_CODE_BIT_LEN 72 +#define ACCESS_CODE_BYTE_LEN 9 +#define SHORTENED_ACCESS_CODE_BIT_LEN 68 + +typedef UINT8 ACCESS_CODE[ACCESS_CODE_BYTE_LEN]; + +#define SYNTH_TX 1 /* want synth code to TRANSMIT at this freq */ +#define SYNTH_RX 2 /* want synth code to RECEIVE at this freq */ + +#define SYNC_REPS 1 /* repeats of sync word transmitted to start of burst */ + +/* Bluetooth CLK27 */ +#define BT_CLK27 (2 << 26) + +/* Bluetooth CLK12 is 1.28 sec */ +#define BT_CLK12_TO_MS(x) ((x) * 1280) +#define BT_MS_TO_CLK12(x) ((x) / 1280) +#define BT_CLK12_TO_SLOTS(x) ((x) << 11) + +/* Bluetooth CLK is 0.625 msec */ +#define BT_CLK_TO_MS(x) (((x) * 5 + 3) / 8) +#define BT_MS_TO_CLK(x) (((x) * 8 + 2) / 5) + +#define BT_CLK_TO_MICROSECS(x) (((x) * 5000 + 3) / 8) +#define BT_MICROSECS_TO_CLK(x) (((x) * 8 + 2499) / 5000) + +/* Maximum UUID size - 16 bytes, and structure to hold any type of UUID. */ +#define MAX_UUID_SIZE 16 +typedef struct +{ +#define LEN_UUID_16 2 +#define LEN_UUID_32 4 +#define LEN_UUID_128 16 + + UINT16 len; + + union + { + UINT16 uuid16; + UINT32 uuid32; + UINT8 uuid128[MAX_UUID_SIZE]; + } uu; + +} tBT_UUID; + +#define BT_EIR_FLAGS_TYPE 0x01 +#define BT_EIR_MORE_16BITS_UUID_TYPE 0x02 +#define BT_EIR_COMPLETE_16BITS_UUID_TYPE 0x03 +#define BT_EIR_MORE_32BITS_UUID_TYPE 0x04 +#define BT_EIR_COMPLETE_32BITS_UUID_TYPE 0x05 +#define BT_EIR_MORE_128BITS_UUID_TYPE 0x06 +#define BT_EIR_COMPLETE_128BITS_UUID_TYPE 0x07 +#define BT_EIR_SHORTENED_LOCAL_NAME_TYPE 0x08 +#define BT_EIR_COMPLETE_LOCAL_NAME_TYPE 0x09 +#define BT_EIR_TX_POWER_LEVEL_TYPE 0x0A +#define BT_EIR_OOB_BD_ADDR_TYPE 0x0C +#define BT_EIR_OOB_COD_TYPE 0x0D +#define BT_EIR_OOB_SSP_HASH_C_TYPE 0x0E +#define BT_EIR_OOB_SSP_RAND_R_TYPE 0x0F +#define BT_EIR_MANUFACTURER_SPECIFIC_TYPE 0xFF + +#define BT_OOB_COD_SIZE 3 +#define BT_OOB_HASH_C_SIZE 16 +#define BT_OOB_RAND_R_SIZE 16 + +/* Broadcom proprietary UUIDs and reserved PSMs +** +** The lowest 4 bytes byte of the UUID or GUID depends on the feature. Typically, +** the value of those bytes will be the PSM or SCN, but it is up to the features. +*/ +#define BRCM_PROPRIETARY_UUID_BASE 0xDA, 0x23, 0x41, 0x02, 0xA3, 0xBB, 0xC1, 0x71, 0xBA, 0x09, 0x6f, 0x21 +#define BRCM_PROPRIETARY_GUID_BASE 0xda23, 0x4102, 0xa3, 0xbb, 0xc1, 0x71, 0xba, 0x09, 0x6f, 0x21 + +/* We will not allocate a PSM in the reserved range to 3rd party apps +*/ +#define BRCM_RESERVED_PSM_START 0x5AE1 +#define BRCM_RESERVED_PSM_END 0x5AFF + +#define BRCM_UTILITY_SERVICE_PSM 0x5AE1 +#define BRCM_MATCHER_PSM 0x5AE3 + +/* Connection statistics +*/ + +/* Structure to hold connection stats */ +#ifndef BT_CONN_STATS_DEFINED +#define BT_CONN_STATS_DEFINED + +/* These bits are used in the bIsConnected field */ +#define BT_CONNECTED_USING_BREDR 1 +#define BT_CONNECTED_USING_AMP 2 + +typedef struct +{ + UINT32 is_connected; + INT32 rssi; + UINT32 bytes_sent; + UINT32 bytes_rcvd; + UINT32 duration; +} tBT_CONN_STATS; + +#endif + + +/***************************************************************************** +** Low Energy definitions +** +** Address types +*/ +#define BLE_ADDR_PUBLIC 0x00 +#define BLE_ADDR_RANDOM 0x01 +#define BLE_ADDR_TYPE_MASK (BLE_ADDR_RANDOM | BLE_ADDR_PUBLIC) +typedef UINT8 tBLE_ADDR_TYPE; + +#define BLE_ADDR_IS_STATIC(x) ((x[0] & 0xC0) == 0xC0) + +typedef struct +{ + tBLE_ADDR_TYPE type; + BD_ADDR bda; +} tBLE_BD_ADDR; + +/* Device Types +*/ +#define BT_DEVICE_TYPE_BREDR 0x01 +#define BT_DEVICE_TYPE_BLE 0x02 +#define BT_DEVICE_TYPE_DUMO 0x03 +typedef UINT8 tBT_DEVICE_TYPE; +/*****************************************************************************/ + + +/* Define trace levels */ +#define BT_TRACE_LEVEL_NONE 0 /* No trace messages to be generated */ +#define BT_TRACE_LEVEL_ERROR 1 /* Error condition trace messages */ +#define BT_TRACE_LEVEL_WARNING 2 /* Warning condition trace messages */ +#define BT_TRACE_LEVEL_API 3 /* API traces */ +#define BT_TRACE_LEVEL_EVENT 4 /* Debug messages for events */ +#define BT_TRACE_LEVEL_DEBUG 5 /* Full debug messages */ +#define BT_TRACE_LEVEL_VERBOSE 6 /* Verbose debug messages */ + +#define MAX_TRACE_LEVEL 6 + + +/* Define New Trace Type Definition */ +/* TRACE_CTRL_TYPE 0x^^000000*/ +#define TRACE_CTRL_MASK 0xff000000 +#define TRACE_GET_CTRL(x) ((((UINT32)(x)) & TRACE_CTRL_MASK) >> 24) + +#define TRACE_CTRL_GENERAL 0x00000000 +#define TRACE_CTRL_STR_RESOURCE 0x01000000 +#define TRACE_CTRL_SEQ_FLOW 0x02000000 +#define TRACE_CTRL_MAX_NUM 3 + +/* LAYER SPECIFIC 0x00^^0000*/ +#define TRACE_LAYER_MASK 0x00ff0000 +#define TRACE_GET_LAYER(x) ((((UINT32)(x)) & TRACE_LAYER_MASK) >> 16) + +#define TRACE_LAYER_NONE 0x00000000 +#define TRACE_LAYER_USB 0x00010000 +#define TRACE_LAYER_SERIAL 0x00020000 +#define TRACE_LAYER_SOCKET 0x00030000 +#define TRACE_LAYER_RS232 0x00040000 +#define TRACE_LAYER_TRANS_MAX_NUM 5 +#define TRACE_LAYER_TRANS_ALL 0x007f0000 +#define TRACE_LAYER_LC 0x00050000 +#define TRACE_LAYER_LM 0x00060000 +#define TRACE_LAYER_HCI 0x00070000 +#define TRACE_LAYER_L2CAP 0x00080000 +#define TRACE_LAYER_RFCOMM 0x00090000 +#define TRACE_LAYER_SDP 0x000a0000 +#define TRACE_LAYER_TCS 0x000b0000 +#define TRACE_LAYER_OBEX 0x000c0000 +#define TRACE_LAYER_BTM 0x000d0000 +#define TRACE_LAYER_GAP 0x000e0000 +#define TRACE_LAYER_DUN 0x000f0000 +#define TRACE_LAYER_GOEP 0x00100000 +#define TRACE_LAYER_ICP 0x00110000 +#define TRACE_LAYER_HSP2 0x00120000 +#define TRACE_LAYER_SPP 0x00130000 +#define TRACE_LAYER_CTP 0x00140000 +#define TRACE_LAYER_BPP 0x00150000 +#define TRACE_LAYER_HCRP 0x00160000 +#define TRACE_LAYER_FTP 0x00170000 +#define TRACE_LAYER_OPP 0x00180000 +#define TRACE_LAYER_BTU 0x00190000 +#define TRACE_LAYER_GKI 0x001a0000 +#define TRACE_LAYER_BNEP 0x001b0000 +#define TRACE_LAYER_PAN 0x001c0000 +#define TRACE_LAYER_HFP 0x001d0000 +#define TRACE_LAYER_HID 0x001e0000 +#define TRACE_LAYER_BIP 0x001f0000 +#define TRACE_LAYER_AVP 0x00200000 +#define TRACE_LAYER_A2D 0x00210000 +#define TRACE_LAYER_SAP 0x00220000 +#define TRACE_LAYER_AMP 0x00230000 +#define TRACE_LAYER_MCA 0x00240000 +#define TRACE_LAYER_ATT 0x00250000 +#define TRACE_LAYER_SMP 0x00260000 +#define TRACE_LAYER_NFC 0x00270000 +#define TRACE_LAYER_NCI 0x00280000 +#define TRACE_LAYER_IDEP 0x00290000 +#define TRACE_LAYER_NDEP 0x002a0000 +#define TRACE_LAYER_LLCP 0x002b0000 +#define TRACE_LAYER_RW 0x002c0000 +#define TRACE_LAYER_CE 0x002d0000 +#define TRACE_LAYER_SNEP 0x002e0000 +#define TRACE_LAYER_NDEF 0x002f0000 +#define TRACE_LAYER_NFA 0x00300000 + +#define TRACE_LAYER_MAX_NUM 0x0031 + + +/* TRACE_ORIGINATOR 0x0000^^00*/ +#define TRACE_ORG_MASK 0x0000ff00 +#define TRACE_GET_ORG(x) ((((UINT32)(x)) & TRACE_ORG_MASK) >> 8) + +#define TRACE_ORG_STACK 0x00000000 +#define TRACE_ORG_HCI_TRANS 0x00000100 +#define TRACE_ORG_PROTO_DISP 0x00000200 +#define TRACE_ORG_RPC 0x00000300 +#define TRACE_ORG_GKI 0x00000400 +#define TRACE_ORG_APPL 0x00000500 +#define TRACE_ORG_SCR_WRAPPER 0x00000600 +#define TRACE_ORG_SCR_ENGINE 0x00000700 +#define TRACE_ORG_USER_SCR 0x00000800 +#define TRACE_ORG_TESTER 0x00000900 +#define TRACE_ORG_MAX_NUM 10 /* 32-bit mask; must be < 32 */ +#define TRACE_LITE_ORG_MAX_NUM 6 +#define TRACE_ORG_ALL 0x03ff +#define TRACE_ORG_RPC_TRANS 0x04 + +#define TRACE_ORG_REG 0x00000909 +#define TRACE_ORG_REG_SUCCESS 0x0000090a + +/* TRACE_TYPE 0x000000^^*/ +#define TRACE_TYPE_MASK 0x000000ff +#define TRACE_GET_TYPE(x) (((UINT32)(x)) & TRACE_TYPE_MASK) + +#define TRACE_TYPE_ERROR 0x00000000 +#define TRACE_TYPE_WARNING 0x00000001 +#define TRACE_TYPE_API 0x00000002 +#define TRACE_TYPE_EVENT 0x00000003 +#define TRACE_TYPE_DEBUG 0x00000004 +#define TRACE_TYPE_STACK_ONLY_MAX TRACE_TYPE_DEBUG +#define TRACE_TYPE_TX 0x00000005 +#define TRACE_TYPE_RX 0x00000006 +#define TRACE_TYPE_DEBUG_ASSERT 0x00000007 +#define TRACE_TYPE_GENERIC 0x00000008 +#define TRACE_TYPE_REG 0x00000009 +#define TRACE_TYPE_REG_SUCCESS 0x0000000a +#define TRACE_TYPE_CMD_TX 0x0000000b +#define TRACE_TYPE_EVT_TX 0x0000000c +#define TRACE_TYPE_ACL_TX 0x0000000d +#define TRACE_TYPE_CMD_RX 0x0000000e +#define TRACE_TYPE_EVT_RX 0x0000000f +#define TRACE_TYPE_ACL_RX 0x00000010 +#define TRACE_TYPE_TARGET_TRACE 0x00000011 +#define TRACE_TYPE_SCO_TX 0x00000012 +#define TRACE_TYPE_SCO_RX 0x00000013 + + +#define TRACE_TYPE_MAX_NUM 20 +#define TRACE_TYPE_ALL 0xffff + +/* Define color for script type */ +#define SCR_COLOR_DEFAULT 0 +#define SCR_COLOR_TYPE_COMMENT 1 +#define SCR_COLOR_TYPE_COMMAND 2 +#define SCR_COLOR_TYPE_EVENT 3 +#define SCR_COLOR_TYPE_SELECT 4 + +/* Define protocol trace flag values */ +#define SCR_PROTO_TRACE_HCI_SUMMARY 0x00000001 +#define SCR_PROTO_TRACE_HCI_DATA 0x00000002 +#define SCR_PROTO_TRACE_L2CAP 0x00000004 +#define SCR_PROTO_TRACE_RFCOMM 0x00000008 +#define SCR_PROTO_TRACE_SDP 0x00000010 +#define SCR_PROTO_TRACE_TCS 0x00000020 +#define SCR_PROTO_TRACE_OBEX 0x00000040 +#define SCR_PROTO_TRACE_OAPP 0x00000080 /* OBEX Application Profile */ +#define SCR_PROTO_TRACE_AMP 0x00000100 +#define SCR_PROTO_TRACE_BNEP 0x00000200 +#define SCR_PROTO_TRACE_AVP 0x00000400 +#define SCR_PROTO_TRACE_MCA 0x00000800 +#define SCR_PROTO_TRACE_ATT 0x00001000 +#define SCR_PROTO_TRACE_SMP 0x00002000 +#define SCR_PROTO_TRACE_NCI 0x00004000 +#define SCR_PROTO_TRACE_DEP 0x00008000 +#define SCR_PROTO_TRACE_LLCP 0x00010000 +#define SCR_PROTO_TRACE_NDEF 0x00020000 +#define SCR_PROTO_TRACE_TAGS 0x00040000 +#define SCR_PROTO_TRACE_ALL 0x0007ffff +#define SCR_PROTO_TRACE_HCI_LOGGING_VSE 0x0800 /* Brcm vs event for logmsg and protocol traces */ + +#define MAX_SCRIPT_TYPE 5 + +#define TCS_PSM_INTERCOM 5 +#define TCS_PSM_CORDLESS 7 +#define BT_PSM_BNEP 0x000F +/* Define PSMs HID uses */ +#define HID_PSM_CONTROL 0x0011 +#define HID_PSM_INTERRUPT 0x0013 + +/* Define a function for logging */ +typedef void (BT_LOG_FUNC) (int trace_type, const char *fmt_str, ...); + +#endif + diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h new file mode 100644 index 0000000..09bb749 --- /dev/null +++ b/stack/include/btm_api.h @@ -0,0 +1,4556 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the Bluetooth Manager (BTM) API function external + * definitions. + * + ******************************************************************************/ +#ifndef BTM_API_H +#define BTM_API_H + +#include "bt_target.h" +#include "sdp_api.h" +#include "hcidefs.h" + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#include "smp_api.h" +#endif +/***************************************************************************** +** DEVICE CONTROL and COMMON +*****************************************************************************/ +/***************************** +** Device Control Constants +******************************/ +/* Maximum number of bytes allowed for vendor specific command parameters */ +#define BTM_MAX_VENDOR_SPECIFIC_LEN HCI_COMMAND_SIZE + +/* BTM application return status codes */ +enum +{ + BTM_SUCCESS = 0, /* 0 Command succeeded */ + BTM_CMD_STARTED, /* 1 Command started OK. */ + BTM_BUSY, /* 2 Device busy with another command */ + BTM_NO_RESOURCES, /* 3 No resources to issue command */ + BTM_MODE_UNSUPPORTED, /* 4 Request for 1 or more unsupported modes */ + BTM_ILLEGAL_VALUE, /* 5 Illegal parameter value */ + BTM_WRONG_MODE, /* 6 Device in wrong mode for request */ + BTM_UNKNOWN_ADDR, /* 7 Unknown remote BD address */ + BTM_DEVICE_TIMEOUT, /* 8 Device timeout */ + BTM_BAD_VALUE_RET, /* 9 A bad value was received from HCI */ + BTM_ERR_PROCESSING, /* 10 Generic error */ + BTM_NOT_AUTHORIZED, /* 11 Authorization failed */ + BTM_DEV_RESET, /* 12 Device has been reset */ + BTM_CMD_STORED, /* 13 request is stored in control block */ + BTM_ILLEGAL_ACTION, /* 14 state machine gets illegal command */ + BTM_DELAY_CHECK, /* 15 delay the check on encryption */ + BTM_SCO_BAD_LENGTH, /* 16 Bad SCO over HCI data length */ + BTM_SUCCESS_NO_SECURITY, /* 17 security passed, no security set */ + BTM_FAILED_ON_SECURITY , /* 18 security failed */ + BTM_REPEATED_ATTEMPTS /* 19 repeated attempts for LE security requests */ +}; +typedef UINT8 tBTM_STATUS; + +/************************* +** Device Control Types +**************************/ +#define BTM_DEVICE_ROLE_BR 0x01 +#define BTM_DEVICE_ROLE_DUAL 0x02 +#define BTM_MAX_DEVICE_ROLE BTM_DEVICE_ROLE_DUAL +typedef UINT8 tBTM_DEVICE_ROLE; + +/* Device name of peer (may be truncated to save space in BTM database) */ +typedef UINT8 tBTM_BD_NAME[BTM_MAX_REM_BD_NAME_LEN + 1]; + +/* Structure returned with local version information */ +typedef struct +{ + UINT8 hci_version; + UINT16 hci_revision; + UINT8 lmp_version; + UINT16 manufacturer; + UINT16 lmp_subversion; +} tBTM_VERSION_INFO; + +/* Structure returned with Vendor Specific Command complete callback */ +typedef struct +{ + UINT16 opcode; + UINT16 param_len; + UINT8 *p_param_buf; +} tBTM_VSC_CMPL; + +#define BTM_VSC_CMPL_DATA_SIZE (BTM_MAX_VENDOR_SPECIFIC_LEN + sizeof(tBTM_VSC_CMPL)) +/************************************************** +** Device Control and General Callback Functions +***************************************************/ +/* Callback function for when device status changes. Appl must poll for +** what the new state is (BTM_IsDeviceUp). The event occurs whenever the stack +** has detected that the controller status has changed. This asynchronous event +** is enabled/disabled by calling BTM_RegisterForDeviceStatusNotif(). +*/ +enum +{ + BTM_DEV_STATUS_UP, + BTM_DEV_STATUS_DOWN, + BTM_DEV_STATUS_CMD_TOUT +}; + +typedef UINT8 tBTM_DEV_STATUS; + + +typedef void (tBTM_DEV_STATUS_CB) (tBTM_DEV_STATUS status); + + +/* Callback function for when a vendor specific event occurs. The length and +** array of returned parameter bytes are included. This asynchronous event +** is enabled/disabled by calling BTM_RegisterForVSEvents(). +*/ +typedef void (tBTM_VS_EVT_CB) (UINT8 len, UINT8 *p); + + +/* General callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_CMPL_CB) (void *p1); + +/* VSC callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_VSC_CMPL_CB) (tBTM_VSC_CMPL *p1); + +/* Callback for apps to check connection and inquiry filters. +** Parameters are the BD Address of remote and the Dev Class of remote. +** If the app returns none zero, the connection or inquiry result will be dropped. +*/ +typedef UINT8 (tBTM_FILTER_CB) (BD_ADDR bd_addr, DEV_CLASS dc); + +/***************************************************************************** +** DEVICE DISCOVERY - Inquiry, Remote Name, Discovery, Class of Device +*****************************************************************************/ +/******************************* +** Device Discovery Constants +********************************/ +/* Discoverable modes */ +#define BTM_NON_DISCOVERABLE 0 +#define BTM_LIMITED_DISCOVERABLE 1 +#define BTM_GENERAL_DISCOVERABLE 2 +#define BTM_DISCOVERABLE_MASK (BTM_LIMITED_DISCOVERABLE|BTM_GENERAL_DISCOVERABLE) +#define BTM_MAX_DISCOVERABLE BTM_GENERAL_DISCOVERABLE +/* high byte for BLE Discoverable modes */ +#define BTM_BLE_NON_DISCOVERABLE 0x0000 +#define BTM_BLE_LIMITED_DISCOVERABLE 0x0100 +#define BTM_BLE_GENERAL_DISCOVERABLE 0x0200 +#define BTM_BLE_MAX_DISCOVERABLE BTM_BLE_GENERAL_DISCOVERABLE +#define BTM_BLE_DISCOVERABLE_MASK (BTM_BLE_NON_DISCOVERABLE|BTM_BLE_LIMITED_DISCOVERABLE|BTM_BLE_GENERAL_DISCOVERABLE) + +/* Connectable modes */ +#define BTM_NON_CONNECTABLE 0 +#define BTM_CONNECTABLE 1 +#define BTM_CONNECTABLE_MASK (BTM_NON_CONNECTABLE | BTM_CONNECTABLE) +/* high byte for BLE Connectable modes */ +#define BTM_BLE_NON_CONNECTABLE 0x0000 +#define BTM_BLE_CONNECTABLE 0x0100 +#define BTM_BLE_MAX_CONNECTABLE BTM_BLE_CONNECTABLE +#define BTM_BLE_CONNECTABLE_MASK (BTM_BLE_NON_CONNECTABLE | BTM_BLE_CONNECTABLE) + +/* Inquiry modes + * Note: These modes are associated with the inquiry active values (BTM_*ACTIVE) */ +#define BTM_GENERAL_INQUIRY 0 +#define BTM_LIMITED_INQUIRY 1 +#define BTM_BR_INQUIRY_MASK 0x0f +/* high byte of inquiry mode for BLE inquiry mode */ +#define BTM_BLE_INQUIRY_NONE 0x00 +#define BTM_BLE_GENERAL_INQUIRY 0x10 +#define BTM_BLE_LIMITED_INQUIRY 0x20 +#define BTM_BLE_INQUIRY_MASK (BTM_BLE_GENERAL_INQUIRY|BTM_BLE_LIMITED_INQUIRY) + +/* BTM_IsInquiryActive return values (Bit Mask) + * Note: These bit masks are associated with the inquiry modes (BTM_*_INQUIRY) */ +#define BTM_INQUIRY_INACTIVE 0x0 /* no inquiry in progress */ +#define BTM_GENERAL_INQUIRY_ACTIVE 0x1 /* a general inquiry is in progress */ +#define BTM_LIMITED_INQUIRY_ACTIVE 0x2 /* a limited inquiry is in progress */ +#define BTM_PERIODIC_INQUIRY_ACTIVE 0x8 /* a periodic inquiry is active */ +#define BTM_SSP_INQUIRY_ACTIVE 0x4 /* SSP is active, so inquiry is disallowed (work around for FW bug) */ + +/* Define scan types */ +#define BTM_SCAN_TYPE_STANDARD 0 +#define BTM_SCAN_TYPE_INTERLACED 1 /* 1.2 devices only */ + +/* Define inquiry results mode */ +#define BTM_INQ_RESULT_STANDARD 0 +#define BTM_INQ_RESULT_WITH_RSSI 1 +#define BTM_INQ_RESULT_EXTENDED 2 + +#define BTM_INQ_RES_IGNORE_RSSI 0x7f /* RSSI value not supplied (ignore it) */ + +/* Inquiry Filter Condition types (see tBTM_INQ_PARMS) */ +#define BTM_CLR_INQUIRY_FILTER 0 /* Inquiry Filtering is turned off */ +#define BTM_FILTER_COND_DEVICE_CLASS HCI_FILTER_COND_DEVICE_CLASS /* Filter on device class */ +#define BTM_FILTER_COND_BD_ADDR HCI_FILTER_COND_BD_ADDR /* Filter on device addr */ + +/* State of the remote name retrieval during inquiry operations. +** Used in the tBTM_INQ_INFO structure, and returned in the +** BTM_InqDbRead, BTM_InqDbFirst, and BTM_InqDbNext functions. +** The name field is valid when the state returned is +** BTM_INQ_RMT_NAME_DONE */ +#define BTM_INQ_RMT_NAME_EMPTY 0 +#define BTM_INQ_RMT_NAME_PENDING 1 +#define BTM_INQ_RMT_NAME_DONE 2 +#define BTM_INQ_RMT_NAME_FAILED 3 + +/********************************* + *** Class of Device constants *** + *********************************/ +#define BTM_FORMAT_TYPE_1 0x00 + +/**************************** +** minor device class field +*****************************/ + +/* 0x00 is used as unclassified for all minor device classes */ +#define BTM_COD_MINOR_UNCLASSIFIED 0x00 + +/* minor device class field for Computer Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_DESKTOP_WORKSTATION 0x04 +#define BTM_COD_MINOR_SERVER_COMPUTER 0x08 +#define BTM_COD_MINOR_LAPTOP 0x0C +#define BTM_COD_MINOR_HANDHELD_PC_PDA 0x10 /* clam shell */ +#define BTM_COD_MINOR_PALM_SIZE_PC_PDA 0x14 +#define BTM_COD_MINOR_WEARABLE_COMPUTER 0x18 /* watch sized */ + +/* minor device class field for Phone Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_CELLULAR 0x04 +#define BTM_COD_MINOR_CORDLESS 0x08 +#define BTM_COD_MINOR_SMART_PHONE 0x0C +#define BTM_COD_MINOR_WIRED_MDM_V_GTWY 0x10 /* wired modem or voice gatway */ +#define BTM_COD_MINOR_ISDN_ACCESS 0x14 + +/* minor device class field for LAN Access Point Major Class */ +/* Load Factor Field bit 5-7 */ +#define BTM_COD_MINOR_FULLY_AVAILABLE 0x00 +#define BTM_COD_MINOR_1_17_UTILIZED 0x20 +#define BTM_COD_MINOR_17_33_UTILIZED 0x40 +#define BTM_COD_MINOR_33_50_UTILIZED 0x60 +#define BTM_COD_MINOR_50_67_UTILIZED 0x80 +#define BTM_COD_MINOR_67_83_UTILIZED 0xA0 +#define BTM_COD_MINOR_83_99_UTILIZED 0xC0 +#define BTM_COD_MINOR_NO_SERVICE_AVAILABLE 0xE0 +/* sub-Field bit 2-4 */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ + +/* minor device class field for Audio/Video Major Class */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_CONFM_HEADSET 0x04 +#define BTM_COD_MINOR_CONFM_HANDSFREE 0x08 +#define BTM_COD_MINOR_MICROPHONE 0x10 +#define BTM_COD_MINOR_LOUDSPEAKER 0x14 +#define BTM_COD_MINOR_HEADPHONES 0x18 +#define BTM_COD_MINOR_PORTABLE_AUDIO 0x1C +#define BTM_COD_MINOR_CAR_AUDIO 0x20 +#define BTM_COD_MINOR_SET_TOP_BOX 0x24 +#define BTM_COD_MINOR_HIFI_AUDIO 0x28 +#define BTM_COD_MINOR_VCR 0x2C +#define BTM_COD_MINOR_VIDEO_CAMERA 0x30 +#define BTM_COD_MINOR_CAMCORDER 0x34 +#define BTM_COD_MINOR_VIDEO_MONITOR 0x38 +#define BTM_COD_MINOR_VIDDISP_LDSPKR 0x3C +#define BTM_COD_MINOR_VIDEO_CONFERENCING 0x40 +#define BTM_COD_MINOR_GAMING_TOY 0x48 + +/* minor device class field for Peripheral Major Class */ +/* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */ +#define BTM_COD_MINOR_KEYBOARD 0x40 +#define BTM_COD_MINOR_POINTING 0x80 +#define BTM_COD_MINOR_COMBO 0xC0 +/* Bits 2-5 OR'd with selection from bits 6-7 */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ +#define BTM_COD_MINOR_JOYSTICK 0x04 +#define BTM_COD_MINOR_GAMEPAD 0x08 +#define BTM_COD_MINOR_REMOTE_CONTROL 0x0C +#define BTM_COD_MINOR_SENSING_DEVICE 0x10 +#define BTM_COD_MINOR_DIGITIZING_TABLET 0x14 +#define BTM_COD_MINOR_CARD_READER 0x18 /* e.g. SIM card reader */ +#define BTM_COD_MINOR_DIGITAL_PAN 0x1C +#define BTM_COD_MINOR_HAND_SCANNER 0x20 +#define BTM_COD_MINOR_HAND_GESTURAL_INPUT 0x24 + +/* minor device class field for Imaging Major Class */ +/* Bits 5-7 independently specify display, camera, scanner, or printer */ +#define BTM_COD_MINOR_DISPLAY 0x10 +#define BTM_COD_MINOR_CAMERA 0x20 +#define BTM_COD_MINOR_SCANNER 0x40 +#define BTM_COD_MINOR_PRINTER 0x80 +/* Bits 2-3 Reserved */ +/* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ + +/* minor device class field for Wearable Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_WRIST_WATCH 0x04 +#define BTM_COD_MINOR_PAGER 0x08 +#define BTM_COD_MINOR_JACKET 0x0C +#define BTM_COD_MINOR_HELMET 0x10 +#define BTM_COD_MINOR_GLASSES 0x14 + +/* minor device class field for Toy Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_ROBOT 0x04 +#define BTM_COD_MINOR_VEHICLE 0x08 +#define BTM_COD_MINOR_DOLL_ACTION_FIGURE 0x0C +#define BTM_COD_MINOR_CONTROLLER 0x10 +#define BTM_COD_MINOR_GAME 0x14 + +/* minor device class field for Health Major Class */ +/* Bits 2-7 meaningful */ +#define BTM_COD_MINOR_BLOOD_MONITOR 0x04 +#define BTM_COD_MINOR_THERMOMETER 0x08 +#define BTM_COD_MINOR_WEIGHING_SCALE 0x0C +#define BTM_COD_MINOR_GLUCOSE_METER 0x10 +#define BTM_COD_MINOR_PULSE_OXIMETER 0x14 +#define BTM_COD_MINOR_HEART_PULSE_MONITOR 0x18 +#define BTM_COD_MINOR_HEALTH_DATA_DISPLAY 0x1C +#define BTM_COD_MINOR_STEP_COUNTER 0x20 +#define BTM_COD_MINOR_BODY_COM_ANALYZER 0x24 +#define BTM_COD_MINOR_PEAK_FLOW_MONITOR 0x28 +#define BTM_COD_MINOR_MEDICATION_MONITOR 0x2C +#define BTM_COD_MINOR_KNEE_PROSTHESIS 0x30 +#define BTM_COD_MINOR_ANKLE_PROSTHESIS 0x34 + + +/*************************** +** major device class field +****************************/ +#define BTM_COD_MAJOR_MISCELLANEOUS 0x00 +#define BTM_COD_MAJOR_COMPUTER 0x01 +#define BTM_COD_MAJOR_PHONE 0x02 +#define BTM_COD_MAJOR_LAN_ACCESS_PT 0x03 +#define BTM_COD_MAJOR_AUDIO 0x04 +#define BTM_COD_MAJOR_PERIPHERAL 0x05 +#define BTM_COD_MAJOR_IMAGING 0x06 +#define BTM_COD_MAJOR_WEARABLE 0x07 +#define BTM_COD_MAJOR_TOY 0x08 +#define BTM_COD_MAJOR_HEALTH 0x09 +#define BTM_COD_MAJOR_UNCLASSIFIED 0x1F + +/*************************** +** service class fields +****************************/ +#define BTM_COD_SERVICE_LMTD_DISCOVER 0x0020 +#define BTM_COD_SERVICE_POSITIONING 0x0100 +#define BTM_COD_SERVICE_NETWORKING 0x0200 +#define BTM_COD_SERVICE_RENDERING 0x0400 +#define BTM_COD_SERVICE_CAPTURING 0x0800 +#define BTM_COD_SERVICE_OBJ_TRANSFER 0x1000 +#define BTM_COD_SERVICE_AUDIO 0x2000 +#define BTM_COD_SERVICE_TELEPHONY 0x4000 +#define BTM_COD_SERVICE_INFORMATION 0x8000 + +/* class of device field macros */ +#define BTM_COD_FORMAT_TYPE(u8, pd) {u8 = pd[2]&0x03;} +#define BTM_COD_MINOR_CLASS(u8, pd) {u8 = pd[2]&0xFC;} +#define BTM_COD_MAJOR_CLASS(u8, pd) {u8 = pd[1]&0x1F;} +#define BTM_COD_SERVICE_CLASS(u16, pd) {u16 = pd[0]; u16<<=8; u16 += pd[1]&0xE0;} + +/* to set the fields (assumes that format type is always 0) */ +#define FIELDS_TO_COD(pd, mn, mj, sv) {pd[2] = mn; pd[1] = \ + mj+ ((sv)&BTM_COD_SERVICE_CLASS_LO_B); \ + pd[0] = (sv) >> 8;} + +/* the COD masks */ +#define BTM_COD_FORMAT_TYPE_MASK 0x03 +#define BTM_COD_MINOR_CLASS_MASK 0xFC +#define BTM_COD_MAJOR_CLASS_MASK 0x1F +#define BTM_COD_SERVICE_CLASS_LO_B 0x00E0 +#define BTM_COD_SERVICE_CLASS_MASK 0xFFE0 + + +/* BTM service definitions +** Used for storing EIR data to bit mask +*/ +#ifndef BTM_EIR_UUID_LKUP_TBL +enum +{ + BTM_EIR_UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER, +/* BTM_EIR_UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR, */ +/* BTM_EIR_UUID_SERVCLASS_PUBLIC_BROWSE_GROUP, */ + BTM_EIR_UUID_SERVCLASS_SERIAL_PORT, + BTM_EIR_UUID_SERVCLASS_LAN_ACCESS_USING_PPP, + BTM_EIR_UUID_SERVCLASS_DIALUP_NETWORKING, + BTM_EIR_UUID_SERVCLASS_IRMC_SYNC, + BTM_EIR_UUID_SERVCLASS_OBEX_OBJECT_PUSH, + BTM_EIR_UUID_SERVCLASS_OBEX_FILE_TRANSFER, + BTM_EIR_UUID_SERVCLASS_IRMC_SYNC_COMMAND, + BTM_EIR_UUID_SERVCLASS_HEADSET, + BTM_EIR_UUID_SERVCLASS_CORDLESS_TELEPHONY, + BTM_EIR_UUID_SERVCLASS_AUDIO_SOURCE, + BTM_EIR_UUID_SERVCLASS_AUDIO_SINK, + BTM_EIR_UUID_SERVCLASS_AV_REM_CTRL_TARGET, +/* BTM_EIR_UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, */ + BTM_EIR_UUID_SERVCLASS_AV_REMOTE_CONTROL, +/* BTM_EIR_UUID_SERVCLASS_VIDEO_CONFERENCING, */ + BTM_EIR_UUID_SERVCLASS_INTERCOM, + BTM_EIR_UUID_SERVCLASS_FAX, + BTM_EIR_UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, +/* BTM_EIR_UUID_SERVCLASS_WAP, */ +/* BTM_EIR_UUID_SERVCLASS_WAP_CLIENT, */ + BTM_EIR_UUID_SERVCLASS_PANU, + BTM_EIR_UUID_SERVCLASS_NAP, + BTM_EIR_UUID_SERVCLASS_GN, + BTM_EIR_UUID_SERVCLASS_DIRECT_PRINTING, +/* BTM_EIR_UUID_SERVCLASS_REFERENCE_PRINTING, */ + BTM_EIR_UUID_SERVCLASS_IMAGING, + BTM_EIR_UUID_SERVCLASS_IMAGING_RESPONDER, + BTM_EIR_UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, + BTM_EIR_UUID_SERVCLASS_IMAGING_REF_OBJECTS, + BTM_EIR_UUID_SERVCLASS_HF_HANDSFREE, + BTM_EIR_UUID_SERVCLASS_AG_HANDSFREE, + BTM_EIR_UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE, +/* BTM_EIR_UUID_SERVCLASS_REFLECTED_UI, */ + BTM_EIR_UUID_SERVCLASS_BASIC_PRINTING, + BTM_EIR_UUID_SERVCLASS_PRINTING_STATUS, + BTM_EIR_UUID_SERVCLASS_HUMAN_INTERFACE, + BTM_EIR_UUID_SERVCLASS_CABLE_REPLACEMENT, + BTM_EIR_UUID_SERVCLASS_HCRP_PRINT, + BTM_EIR_UUID_SERVCLASS_HCRP_SCAN, +/* BTM_EIR_UUID_SERVCLASS_COMMON_ISDN_ACCESS, */ +/* BTM_EIR_UUID_SERVCLASS_VIDEO_CONFERENCING_GW, */ +/* BTM_EIR_UUID_SERVCLASS_UDI_MT, */ +/* BTM_EIR_UUID_SERVCLASS_UDI_TA, */ +/* BTM_EIR_UUID_SERVCLASS_VCP, */ + BTM_EIR_UUID_SERVCLASS_SAP, + BTM_EIR_UUID_SERVCLASS_PBAP_PCE, + BTM_EIR_UUID_SERVCLASS_PBAP_PSE, +/* BTM_EIR_UUID_SERVCLASS_TE_PHONE_ACCESS, */ +/* BTM_EIR_UUID_SERVCLASS_ME_PHONE_ACCESS, */ + BTM_EIR_UUID_SERVCLASS_PHONE_ACCESS, + BTM_EIR_UUID_SERVCLASS_HEADSET_HS, + BTM_EIR_UUID_SERVCLASS_PNP_INFORMATION, +/* BTM_EIR_UUID_SERVCLASS_GENERIC_NETWORKING, */ +/* BTM_EIR_UUID_SERVCLASS_GENERIC_FILETRANSFER, */ +/* BTM_EIR_UUID_SERVCLASS_GENERIC_AUDIO, */ +/* BTM_EIR_UUID_SERVCLASS_GENERIC_TELEPHONY, */ +/* BTM_EIR_UUID_SERVCLASS_UPNP_SERVICE, */ +/* BTM_EIR_UUID_SERVCLASS_UPNP_IP_SERVICE, */ +/* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_PAN, */ +/* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_LAP, */ +/* BTM_EIR_UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP, */ + BTM_EIR_UUID_SERVCLASS_VIDEO_SOURCE, + BTM_EIR_UUID_SERVCLASS_VIDEO_SINK, +/* BTM_EIR_UUID_SERVCLASS_VIDEO_DISTRIBUTION */ +/* BTM_EIR_UUID_SERVCLASS_HDP_PROFILE */ + BTM_EIR_UUID_SERVCLASS_MESSAGE_ACCESS, + BTM_EIR_UUID_SERVCLASS_MESSAGE_NOTIFICATION, + BTM_EIR_UUID_SERVCLASS_HDP_SOURCE, + BTM_EIR_UUID_SERVCLASS_HDP_SINK, + BTM_EIR_MAX_SERVICES +}; +#endif /* BTM_EIR_UUID_LKUP_TBL */ + +/* search result in EIR of inquiry database */ +#define BTM_EIR_FOUND 0 +#define BTM_EIR_NOT_FOUND 1 +#define BTM_EIR_UNKNOWN 2 + +typedef UINT8 tBTM_EIR_SEARCH_RESULT; + +#define BTM_EIR_FLAGS_TYPE HCI_EIR_FLAGS_TYPE /* 0x01 */ +#define BTM_EIR_MORE_16BITS_UUID_TYPE HCI_EIR_MORE_16BITS_UUID_TYPE /* 0x02 */ +#define BTM_EIR_COMPLETE_16BITS_UUID_TYPE HCI_EIR_COMPLETE_16BITS_UUID_TYPE /* 0x03 */ +#define BTM_EIR_MORE_32BITS_UUID_TYPE HCI_EIR_MORE_32BITS_UUID_TYPE /* 0x04 */ +#define BTM_EIR_COMPLETE_32BITS_UUID_TYPE HCI_EIR_COMPLETE_32BITS_UUID_TYPE /* 0x05 */ +#define BTM_EIR_MORE_128BITS_UUID_TYPE HCI_EIR_MORE_128BITS_UUID_TYPE /* 0x06 */ +#define BTM_EIR_COMPLETE_128BITS_UUID_TYPE HCI_EIR_COMPLETE_128BITS_UUID_TYPE /* 0x07 */ +#define BTM_EIR_SHORTENED_LOCAL_NAME_TYPE HCI_EIR_SHORTENED_LOCAL_NAME_TYPE /* 0x08 */ +#define BTM_EIR_COMPLETE_LOCAL_NAME_TYPE HCI_EIR_COMPLETE_LOCAL_NAME_TYPE /* 0x09 */ +#define BTM_EIR_TX_POWER_LEVEL_TYPE HCI_EIR_TX_POWER_LEVEL_TYPE /* 0x0A */ +#define BTM_EIR_MANUFACTURER_SPECIFIC_TYPE HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xFF */ + +/* the following EIR tags are defined to OOB, not regular EIR data */ +#define BTM_EIR_OOB_BD_ADDR_TYPE HCI_EIR_OOB_BD_ADDR_TYPE /* 6 bytes */ +#define BTM_EIR_OOB_COD_TYPE HCI_EIR_OOB_COD_TYPE /* 3 bytes */ +#define BTM_EIR_OOB_SSP_HASH_C_TYPE HCI_EIR_OOB_SSP_HASH_C_TYPE /* 16 bytes */ +#define BTM_EIR_OOB_SSP_RAND_R_TYPE HCI_EIR_OOB_SSP_RAND_R_TYPE /* 16 bytes */ + +#define BTM_OOB_MANDATORY_SIZE 8 /* include 2 bytes length & 6 bytes bd_addr */ +#define BTM_OOB_DATA_LEN_SIZE 2 +#define BTM_OOB_BD_ADDR_SIZE 6 +#define BTM_OOB_COD_SIZE BT_OOB_COD_SIZE +#define BTM_OOB_HASH_C_SIZE BT_OOB_HASH_C_SIZE +#define BTM_OOB_RAND_R_SIZE BT_OOB_RAND_R_SIZE + + +#if BLE_INCLUDED == TRUE +#define BTM_BLE_SEC_NONE 0 +#define BTM_BLE_SEC_ENCRYPT 1 /* encrypt the link using current key */ +#define BTM_BLE_SEC_ENCRYPT_NO_MITM 2 +#define BTM_BLE_SEC_ENCRYPT_MITM 3 +typedef UINT8 tBTM_BLE_SEC_ACT; +#endif +/************************************************************************************************ +** BTM Services MACROS handle array of UINT32 bits for more than 32 services +*************************************************************************************************/ +/* Determine the number of UINT32's necessary for services */ +#define BTM_EIR_ARRAY_BITS 32 /* Number of bits in each array element */ +#define BTM_EIR_SERVICE_ARRAY_SIZE (((UINT32)BTM_EIR_MAX_SERVICES / BTM_EIR_ARRAY_BITS) + \ + (((UINT32)BTM_EIR_MAX_SERVICES % BTM_EIR_ARRAY_BITS) ? 1 : 0)) + +/* MACRO to set the service bit mask in a bit stream */ +#define BTM_EIR_SET_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] |= \ + ((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) + + +/* MACRO to clear the service bit mask in a bit stream */ +#define BTM_EIR_CLR_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] &= \ + ~((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) + +/* MACRO to check the service bit mask in a bit stream */ +#define BTM_EIR_HAS_SERVICE(p, service) ((((UINT32 *)(p))[(((UINT32)(service)) / BTM_EIR_ARRAY_BITS)] & \ + ((UINT32)1 << (((UINT32)(service)) % BTM_EIR_ARRAY_BITS))) >> (((UINT32)(service)) % BTM_EIR_ARRAY_BITS)) + +/* start of EIR in HCI buffer, 4 bytes = HCI Command(2) + Length(1) + FEC_Req(1) */ +#define BTM_HCI_EIR_OFFSET (BT_HDR_SIZE + 4) + +/*************************** +** Device Discovery Types +****************************/ +/* Definitions of the parameters passed to BTM_StartInquiry and +** BTM_SetPeriodicInquiryMode. +*/ +typedef struct /* contains the two device class condition fields */ +{ + DEV_CLASS dev_class; + DEV_CLASS dev_class_mask; +} tBTM_COD_COND; + + +typedef union /* contains the inquiry filter condition */ +{ + BD_ADDR bdaddr_cond; + tBTM_COD_COND cod_cond; +} tBTM_INQ_FILT_COND; + + +typedef struct /* contains the parameters passed to the inquiry functions */ +{ + UINT8 mode; /* general or limited */ + UINT8 duration; /* duration of the inquiry (1.28 sec increments) */ + UINT8 max_resps; /* maximum number of responses to return */ + BOOLEAN report_dup; /* report duplicated inquiry response with higher RSSI value */ + UINT8 filter_cond_type; /* new devices, BD ADDR, COD, or No filtering */ + tBTM_INQ_FILT_COND filter_cond; /* filter value based on filter cond type */ +} tBTM_INQ_PARMS; + +#define BTM_INQ_RESULT_BR 0x01 +#define BTM_INQ_RESULT_BLE 0x02 + +#if (BLE_INCLUDED == TRUE) +#define BTM_BLE_EVT_CONN_ADV 0x00 +#define BTM_BLE_EVT_CONN_DIR_ADV 0x01 +#define BTM_BLE_EVT_DISC_ADV 0x02 +#define BTM_BLE_EVT_NON_CONN_ADV 0x03 +#define BTM_BLE_EVT_SCAN_RSP 0x04 +typedef UINT8 tBTM_BLE_EVT_TYPE; +#endif + +/* These are the fields returned in each device's response to the inquiry. It +** is returned in the results callback if registered. +*/ +typedef struct +{ + UINT16 clock_offset; + BD_ADDR remote_bd_addr; + DEV_CLASS dev_class; + UINT8 page_scan_rep_mode; + UINT8 page_scan_per_mode; + UINT8 page_scan_mode; + INT8 rssi; /* Set to BTM_INQ_RES_IGNORE_RSSI if not valid */ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT32 eir_uuid[BTM_EIR_SERVICE_ARRAY_SIZE]; + BOOLEAN eir_complete_list; +#endif +#if (BLE_INCLUDED == TRUE) + tBT_DEVICE_TYPE device_type; + UINT8 inq_result_type; + UINT8 ble_addr_type; + tBTM_BLE_EVT_TYPE ble_evt_type; + UINT8 flag; +#endif +} tBTM_INQ_RESULTS; + + +/* This is the inquiry response information held in its database by BTM, and available +** to applications via BTM_InqDbRead, BTM_InqDbFirst, and BTM_InqDbNext. +*/ +typedef struct +{ + tBTM_INQ_RESULTS results; + + BOOLEAN appl_knows_rem_name; /* set by application if it knows the remote name of the peer device. + This is later used by application to determine if remote name request is + required to be done. Having the flag here avoid duplicate store of inquiry results */ + + +#if (BTM_INQ_GET_REMOTE_NAME == TRUE || BLE_INCLUDED == TRUE) + UINT16 remote_name_len; + tBTM_BD_NAME remote_name; + UINT8 remote_name_state; + UINT8 remote_name_type; +#endif + +} tBTM_INQ_INFO; + + +/* Structure returned with inquiry complete callback */ +typedef struct +{ + tBTM_STATUS status; + UINT8 num_resp; /* Number of results from the current inquiry */ +} tBTM_INQUIRY_CMPL; + + +/* Structure returned with remote name request */ +typedef struct +{ + UINT16 status; + UINT16 length; + BD_NAME remote_bd_name; +} tBTM_REMOTE_DEV_NAME; + +typedef struct +{ + UINT8 pcm_intf_rate; /* PCM interface rate: 0: 128kbps, 1: 256 kbps; + 2:512 bps; 3: 1024kbps; 4: 2048kbps */ + UINT8 frame_type; /* frame type: 0: short; 1: long */ + UINT8 sync_mode; /* sync mode: 0: slave; 1: master */ + UINT8 clock_mode; /* clock mode: 0: slave; 1: master */ + +}tBTM_SCO_PCM_PARAM; + +/**************************************** +** Device Discovery Callback Functions +*****************************************/ +/* Callback function for asynchronous notifications when the BTM inquiry DB +** changes. First param is inquiry database, second is if added to or removed +** from the inquiry database. +*/ +typedef void (tBTM_INQ_DB_CHANGE_CB) (void *p1, BOOLEAN is_new); + +/* Callback function for notifications when the BTM gets inquiry response. +** First param is inquiry results database, second is pointer of EIR. +*/ +typedef void (tBTM_INQ_RESULTS_CB) (tBTM_INQ_RESULTS *p_inq_results, UINT8 *p_eir); + +/***************************************************************************** +** ACL CHANNEL MANAGEMENT +*****************************************************************************/ +/****************** +** ACL Constants +*******************/ + +/* ACL modes */ +#define BTM_ACL_MODE_NORMAL HCI_MODE_ACTIVE +#define BTM_ACL_MODE_HOLD HCI_MODE_HOLD +#define BTM_ACL_MODE_SNIFF HCI_MODE_SNIFF +#define BTM_ACL_MODE_PARK HCI_MODE_PARK + +/* Returned with structure in role switch callback (tBTM_ROLE_SWITCH_CMPL) */ +#define BTM_ROLE_MASTER HCI_ROLE_MASTER +#define BTM_ROLE_SLAVE HCI_ROLE_SLAVE +#define BTM_ROLE_UNDEFINED 0xff /* undefined value (error status) */ + +/* ACL Packet Types */ +#define BTM_ACL_PKT_TYPES_MASK_DM1 HCI_PKT_TYPES_MASK_DM1 +#define BTM_ACL_PKT_TYPES_MASK_DH1 HCI_PKT_TYPES_MASK_DH1 +#define BTM_ACL_PKT_TYPES_MASK_DM3 HCI_PKT_TYPES_MASK_DM3 +#define BTM_ACL_PKT_TYPES_MASK_DH3 HCI_PKT_TYPES_MASK_DH3 +#define BTM_ACL_PKT_TYPES_MASK_DM5 HCI_PKT_TYPES_MASK_DM5 +#define BTM_ACL_PKT_TYPES_MASK_DH5 HCI_PKT_TYPES_MASK_DH5 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 HCI_PKT_TYPES_MASK_NO_2_DH1 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 HCI_PKT_TYPES_MASK_NO_3_DH1 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 HCI_PKT_TYPES_MASK_NO_2_DH3 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 HCI_PKT_TYPES_MASK_NO_3_DH3 +#define BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 HCI_PKT_TYPES_MASK_NO_2_DH5 +#define BTM_ACL_PKT_TYPES_MASK_NO_3_DH5 HCI_PKT_TYPES_MASK_NO_3_DH5 + +/*************** +** ACL Types +****************/ + +/* Structure returned with link policy information (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadLinkPolicy call. +*/ +typedef struct +{ + tBTM_STATUS status; + UINT8 hci_status; + BD_ADDR rem_bda; + UINT16 settings; +} tBTM_LNK_POLICY_RESULTS; + +/* Structure returned with Role Switch information (in tBTM_CMPL_CB callback function) +** in response to BTM_SwitchRole call. +*/ +typedef struct +{ + UINT8 hci_status; /* HCI status returned with the event */ + UINT8 role; /* BTM_ROLE_MASTER or BTM_ROLE_SLAVE */ + BD_ADDR remote_bd_addr; /* Remote BD addr involved with the switch */ +} tBTM_ROLE_SWITCH_CMPL; + + +/* Structure returned with Change Link Key information (in tBTM_CMPL_CB callback function) +** in response to BTM_ChangeLinkKey call. +*/ +typedef struct +{ + UINT8 hci_status; /* HCI status returned with the event */ + BD_ADDR remote_bd_addr; /* Remote BD addr involved with the change link key */ +} tBTM_CHANGE_KEY_CMPL; + + +/* Structure returned with QoS information (in tBTM_CMPL_CB callback function) +** in response to BTM_SetQoS call. +*/ +typedef struct +{ + FLOW_SPEC flow; + UINT16 handle; + UINT8 status; +} tBTM_QOS_SETUP_CMPL; + + +/* Structure returned with read RSSI event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadRSSI call. +*/ +typedef struct +{ + tBTM_STATUS status; + UINT8 hci_status; + INT8 rssi; + BD_ADDR rem_bda; +} tBTM_RSSI_RESULTS; + +/* Structure returned with read current TX power event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadTxPower call. +*/ +typedef struct +{ + tBTM_STATUS status; + UINT8 hci_status; + INT8 tx_power; + BD_ADDR rem_bda; +} tBTM_TX_POWER_RESULTS; + +/* Structure returned with read link quality event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadLinkQuality call. +*/ +typedef struct +{ + tBTM_STATUS status; + UINT8 hci_status; + UINT8 link_quality; + BD_ADDR rem_bda; +} tBTM_LINK_QUALITY_RESULTS; + +/* Structure returned with read inq tx power quality event (in tBTM_CMPL_CB callback function) +** in response to BTM_ReadInquiryRspTxPower call. +*/ +typedef struct +{ + tBTM_STATUS status; + UINT8 hci_status; + INT8 tx_power; +} tBTM_INQ_TXPWR_RESULTS; + +enum +{ + BTM_BL_CONN_EVT, + BTM_BL_DISCN_EVT, + BTM_BL_UPDATE_EVT, + BTM_BL_ROLE_CHG_EVT, + BTM_BL_COLLISION_EVT +}; +typedef UINT8 tBTM_BL_EVENT; +typedef UINT16 tBTM_BL_EVENT_MASK; + +#define BTM_BL_CONN_MASK 0x0001 +#define BTM_BL_DISCN_MASK 0x0002 +#define BTM_BL_UPDATE_MASK 0x0004 +#define BTM_BL_ROLE_CHG_MASK 0x0008 + +/* the data type associated with BTM_BL_CONN_EVT */ +typedef struct +{ + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the newly connected device */ + DEV_CLASS_PTR p_dc; /* The device class */ + BD_NAME_PTR p_bdn; /* The device name */ + UINT8 *p_features; /* The remote device's supported features */ +} tBTM_BL_CONN_DATA; + +/* the data type associated with BTM_BL_DISCN_EVT */ +typedef struct +{ + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the disconnected device */ +} tBTM_BL_DISCN_DATA; + +/* Busy-Level shall have the inquiry_paging mask set when + * inquiry/paging is in progress, Else the number of ACL links */ +#define BTM_BL_INQUIRY_PAGING_MASK 0x10 +#define BTM_BL_INQUIRY_STARTED (BTM_BL_INQUIRY_PAGING_MASK | 0x1) +#define BTM_BL_INQUIRY_CANCELLED (BTM_BL_INQUIRY_PAGING_MASK | 0x2) +#define BTM_BL_INQUIRY_COMPLETE (BTM_BL_INQUIRY_PAGING_MASK | 0x3) +#define BTM_BL_PAGING_STARTED (BTM_BL_INQUIRY_PAGING_MASK | 0x4) +#define BTM_BL_PAGING_COMPLETE (BTM_BL_INQUIRY_PAGING_MASK | 0x5) +/* the data type associated with BTM_BL_UPDATE_EVT */ +typedef struct +{ + tBTM_BL_EVENT event; /* The event reported. */ + UINT8 busy_level;/* when paging or inquiring, level is as above. + * Otherwise, the number of ACL links. */ +} tBTM_BL_UPDATE_DATA; + +/* the data type associated with BTM_BL_ROLE_CHG_EVT */ +typedef struct +{ + tBTM_BL_EVENT event; /* The event reported. */ + BD_ADDR_PTR p_bda; /* The address of the peer connected device */ + UINT8 new_role; + UINT8 hci_status; /* HCI status returned with the event */ +} tBTM_BL_ROLE_CHG_DATA; + +typedef union +{ + tBTM_BL_EVENT event; /* The event reported. */ + tBTM_BL_CONN_DATA conn; /* The data associated with BTM_BL_CONN_EVT */ + tBTM_BL_DISCN_DATA discn; /* The data associated with BTM_BL_DISCN_EVT */ + tBTM_BL_UPDATE_DATA update; /* The data associated with BTM_BL_UPDATE_EVT */ + tBTM_BL_ROLE_CHG_DATA role_chg;/*The data associated with BTM_BL_ROLE_CHG_EVT */ +} tBTM_BL_EVENT_DATA; + +/* Callback function for notifications when the BTM busy level +** changes. +*/ +typedef void (tBTM_BL_CHANGE_CB) (tBTM_BL_EVENT_DATA *p_data); + +/*************************** +** ACL Callback Functions +****************************/ +/* Callback function for notifications when the BTM ACL connection DB +** changes. First param is BD address, second is if added or removed. +** Registered through BTM_AclRegisterForChanges call. +*/ +typedef void (tBTM_ACL_DB_CHANGE_CB) (BD_ADDR p_bda, DEV_CLASS p_dc, + BD_NAME p_bdn, BD_FEATURES features, + BOOLEAN is_new); + +/***************************************************************************** +** SCO CHANNEL MANAGEMENT +*****************************************************************************/ +/****************** +** SCO Constants +*******************/ + +/* Define an invalid SCO index and an invalid HCI handle */ +#define BTM_INVALID_SCO_INDEX 0xFFFF +#define BTM_INVALID_HCI_HANDLE 0xFFFF + +/* Define an invalid SCO disconnect reason */ +#define BTM_INVALID_SCO_DISC_REASON 0xFFFF + +/* Define SCO packet types used in APIs */ +#define BTM_SCO_PKT_TYPES_MASK_HV1 HCI_ESCO_PKT_TYPES_MASK_HV1 +#define BTM_SCO_PKT_TYPES_MASK_HV2 HCI_ESCO_PKT_TYPES_MASK_HV2 +#define BTM_SCO_PKT_TYPES_MASK_HV3 HCI_ESCO_PKT_TYPES_MASK_HV3 +#define BTM_SCO_PKT_TYPES_MASK_EV3 HCI_ESCO_PKT_TYPES_MASK_EV3 +#define BTM_SCO_PKT_TYPES_MASK_EV4 HCI_ESCO_PKT_TYPES_MASK_EV4 +#define BTM_SCO_PKT_TYPES_MASK_EV5 HCI_ESCO_PKT_TYPES_MASK_EV5 +#define BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 +#define BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 +#define BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 +#define BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5 + +#define BTM_SCO_LINK_ONLY_MASK (BTM_SCO_PKT_TYPES_MASK_HV1 | \ + BTM_SCO_PKT_TYPES_MASK_HV2 | \ + BTM_SCO_PKT_TYPES_MASK_HV3) + +#define BTM_ESCO_LINK_ONLY_MASK (BTM_SCO_PKT_TYPES_MASK_EV3 | \ + BTM_SCO_PKT_TYPES_MASK_EV4 | \ + BTM_SCO_PKT_TYPES_MASK_EV5) + +#define BTM_SCO_LINK_ALL_PKT_MASK (BTM_SCO_LINK_ONLY_MASK | \ + BTM_ESCO_LINK_ONLY_MASK) + +#define BTM_VALID_SCO_ALL_PKT_TYPE HCI_VALID_SCO_ALL_PKT_TYPE + +/* Passed in BTM_CreateSco if the packet type parameter should be ignored */ +#define BTM_IGNORE_SCO_PKT_TYPE 0 + +/*************** +** SCO Types +****************/ +#define BTM_LINK_TYPE_SCO HCI_LINK_TYPE_SCO +#define BTM_LINK_TYPE_ESCO HCI_LINK_TYPE_ESCO +typedef UINT8 tBTM_SCO_TYPE; + + +/******************* +** SCO Routing Path +********************/ +#define BTM_SCO_ROUTE_PCM HCI_BRCM_SCO_ROUTE_PCM +#define BTM_SCO_ROUTE_HCI HCI_BRCM_SCO_ROUTE_HCI +typedef UINT8 tBTM_SCO_ROUTE_TYPE; + + +/******************* +** SCO Codec Types +********************/ +#define BTM_SCO_CODEC_NONE 0x0000 +#define BTM_SCO_CODEC_CVSD 0x0001 +#define BTM_SCO_CODEC_MSBC 0x0002 +typedef UINT16 tBTM_SCO_CODEC_TYPE; + +/******************* +** SCO Voice Settings +********************/ +#define BTM_VOICE_SETTING_CVSD ((UINT16) (HCI_INP_CODING_LINEAR | \ + HCI_INP_DATA_FMT_2S_COMPLEMENT | \ + HCI_INP_SAMPLE_SIZE_16BIT | \ + HCI_AIR_CODING_FORMAT_CVSD)) + +#define BTM_VOICE_SETTING_TRANS ((UINT16) (HCI_INP_CODING_LINEAR | \ + HCI_INP_DATA_FMT_2S_COMPLEMENT | \ + HCI_INP_SAMPLE_SIZE_16BIT | \ + HCI_AIR_CODING_FORMAT_TRANSPNT)) + +/******************* +** SCO Data Status +********************/ +enum +{ + BTM_SCO_DATA_CORRECT, + BTM_SCO_DATA_PAR_ERR, + BTM_SCO_DATA_NONE, + BTM_SCO_DATA_PAR_LOST +}; +typedef UINT8 tBTM_SCO_DATA_FLAG; + +/*************************** +** SCO Callback Functions +****************************/ +typedef void (tBTM_SCO_CB) (UINT16 sco_inx); +typedef void (tBTM_SCO_DATA_CB) (UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status); + +/****************** +** eSCO Constants +*******************/ +#define BTM_64KBITS_RATE 0x00001f40 /* 64 kbits/sec data rate */ + +/* Retransmission effort */ +#define BTM_ESCO_RETRANS_OFF 0 +#define BTM_ESCO_RETRANS_POWER 1 +#define BTM_ESCO_RETRANS_QUALITY 2 +#define BTM_ESCO_RETRANS_DONTCARE 0xff + +/* Max Latency Don't Care */ +#define BTM_ESCO_MAX_LAT_DONTCARE 0xffff + +/*************** +** eSCO Types +****************/ +/* tBTM_ESCO_CBACK event types */ +#define BTM_ESCO_CHG_EVT 1 +#define BTM_ESCO_CONN_REQ_EVT 2 +typedef UINT8 tBTM_ESCO_EVT; + +/* Passed into BTM_SetEScoMode() */ +typedef struct +{ + UINT32 tx_bw; + UINT32 rx_bw; + UINT16 max_latency; + UINT16 voice_contfmt; /* Voice Settings or Content Format */ + UINT16 packet_types; + UINT8 retrans_effort; +} tBTM_ESCO_PARAMS; + +typedef struct +{ + UINT16 max_latency; + UINT16 packet_types; + UINT8 retrans_effort; +} tBTM_CHG_ESCO_PARAMS; + +/* Returned by BTM_ReadEScoLinkParms() */ +typedef struct +{ + UINT16 rx_pkt_len; + UINT16 tx_pkt_len; + BD_ADDR bd_addr; + UINT8 link_type; /* BTM_LINK_TYPE_SCO or BTM_LINK_TYPE_ESCO */ + UINT8 tx_interval; + UINT8 retrans_window; + UINT8 air_mode; +} tBTM_ESCO_DATA; + +typedef struct +{ + UINT16 sco_inx; + UINT16 rx_pkt_len; + UINT16 tx_pkt_len; + BD_ADDR bd_addr; + UINT8 hci_status; + UINT8 tx_interval; + UINT8 retrans_window; +} tBTM_CHG_ESCO_EVT_DATA; + +typedef struct +{ + UINT16 sco_inx; + BD_ADDR bd_addr; + DEV_CLASS dev_class; + tBTM_SCO_TYPE link_type; +} tBTM_ESCO_CONN_REQ_EVT_DATA; + +typedef union +{ + tBTM_CHG_ESCO_EVT_DATA chg_evt; + tBTM_ESCO_CONN_REQ_EVT_DATA conn_evt; +} tBTM_ESCO_EVT_DATA; + +/*************************** +** eSCO Callback Functions +****************************/ +typedef void (tBTM_ESCO_CBACK) (tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data); + + +/***************************************************************************** +** SECURITY MANAGEMENT +*****************************************************************************/ +/******************************* +** Security Manager Constants +********************************/ + +/* Security Mode (BTM_SetSecurityMode) */ +#define BTM_SEC_MODE_UNDEFINED 0 +#define BTM_SEC_MODE_NONE 1 +#define BTM_SEC_MODE_SERVICE 2 +#define BTM_SEC_MODE_LINK 3 +#define BTM_SEC_MODE_SP 4 +#define BTM_SEC_MODE_SP_DEBUG 5 + +/* Security Service Levels [bit mask] (BTM_SetSecurityLevel) +** Encryption should not be used without authentication +*/ +#define BTM_SEC_NONE 0x0000 /* Nothing required */ +#define BTM_SEC_IN_AUTHORIZE 0x0001 /* Inbound call requires authorization */ +#define BTM_SEC_IN_AUTHENTICATE 0x0002 /* Inbound call requires authentication */ +#define BTM_SEC_IN_ENCRYPT 0x0004 /* Inbound call requires encryption */ +#define BTM_SEC_OUT_AUTHORIZE 0x0008 /* Outbound call requires authorization */ +#define BTM_SEC_OUT_AUTHENTICATE 0x0010 /* Outbound call requires authentication */ +#define BTM_SEC_OUT_ENCRYPT 0x0020 /* Outbound call requires encryption */ +#define BTM_SEC_BOND 0x0040 /* Bonding */ +#define BTM_SEC_BOND_CONN 0x0080 /* bond_created_connection */ +#define BTM_SEC_FORCE_MASTER 0x0100 /* Need to switch connection to be master */ +#define BTM_SEC_ATTEMPT_MASTER 0x0200 /* Try to switch connection to be master */ +#define BTM_SEC_FORCE_SLAVE 0x0400 /* Need to switch connection to be master */ +#define BTM_SEC_ATTEMPT_SLAVE 0x0800 /* Try to switch connection to be slave */ +#define BTM_SEC_IN_MITM 0x1000 /* inbound Do man in the middle protection */ +#define BTM_SEC_OUT_MITM 0x2000 /* outbound Do man in the middle protection */ + +/* Security Flags [bit mask] (BTM_GetSecurityFlags) +*/ +#define BTM_SEC_FLAG_AUTHORIZED 0x01 +#define BTM_SEC_FLAG_AUTHENTICATED 0x02 +#define BTM_SEC_FLAG_ENCRYPTED 0x04 +#define BTM_SEC_FLAG_LKEY_KNOWN 0x10 +#define BTM_SEC_FLAG_LKEY_AUTHED 0x20 + +/* PIN types */ +#define BTM_PIN_TYPE_VARIABLE HCI_PIN_TYPE_VARIABLE +#define BTM_PIN_TYPE_FIXED HCI_PIN_TYPE_FIXED + +/* Link Key types used to generate the new link key. +** returned in link key notification callback function +*/ +#define BTM_LKEY_TYPE_COMBINATION HCI_LKEY_TYPE_COMBINATION +#define BTM_LKEY_TYPE_LOCAL_UNIT HCI_LKEY_TYPE_LOCAL_UNIT +#define BTM_LKEY_TYPE_REMOTE_UNIT HCI_LKEY_TYPE_REMOTE_UNIT +#define BTM_LKEY_TYPE_DEBUG_COMB HCI_LKEY_TYPE_DEBUG_COMB +#define BTM_LKEY_TYPE_UNAUTH_COMB HCI_LKEY_TYPE_UNAUTH_COMB +#define BTM_LKEY_TYPE_AUTH_COMB HCI_LKEY_TYPE_AUTH_COMB +#define BTM_LKEY_TYPE_CHANGED_COMB HCI_LKEY_TYPE_CHANGED_COMB +#define BTM_LKEY_TYPE_IGNORE 0xff /* used when event is response from + hci return link keys request */ + +/* Protocol level security (BTM_SetSecurityLevel) */ +#define BTM_SEC_PROTO_L2CAP 0 +#define BTM_SEC_PROTO_SDP 1 +#define BTM_SEC_PROTO_TCS 2 +#define BTM_SEC_PROTO_RFCOMM 3 +#define BTM_SEC_PROTO_OBEX 4 +#define BTM_SEC_PROTO_BNEP 5 +#define BTM_SEC_PROTO_HID 6 /* HID */ +#define BTM_SEC_PROTO_AVDT 7 +#define BTM_SEC_PROTO_MCA 8 + +/* Determine the number of UINT32's necessary for security services */ +#define BTM_SEC_ARRAY_BITS 32 /* Number of bits in each array element */ +#define BTM_SEC_SERVICE_ARRAY_SIZE (((UINT32)BTM_SEC_MAX_SERVICES / BTM_SEC_ARRAY_BITS) + \ + (((UINT32)BTM_SEC_MAX_SERVICES % BTM_SEC_ARRAY_BITS) ? 1 : 0)) + +/* Security service definitions (BTM_SetSecurityLevel) +** Used for Authorization APIs +*/ +#define BTM_SEC_SERVICE_SDP_SERVER 0 +#define BTM_SEC_SERVICE_SERIAL_PORT 1 +#define BTM_SEC_SERVICE_LAN_ACCESS 2 +#define BTM_SEC_SERVICE_DUN 3 +#define BTM_SEC_SERVICE_IRMC_SYNC 4 +#define BTM_SEC_SERVICE_IRMC_SYNC_CMD 5 +#define BTM_SEC_SERVICE_OBEX 6 +#define BTM_SEC_SERVICE_OBEX_FTP 7 +#define BTM_SEC_SERVICE_HEADSET 8 +#define BTM_SEC_SERVICE_CORDLESS 9 +#define BTM_SEC_SERVICE_INTERCOM 10 +#define BTM_SEC_SERVICE_FAX 11 +#define BTM_SEC_SERVICE_HEADSET_AG 12 +#define BTM_SEC_SERVICE_PNP_INFO 13 +#define BTM_SEC_SERVICE_GEN_NET 14 +#define BTM_SEC_SERVICE_GEN_FILE 15 +#define BTM_SEC_SERVICE_GEN_AUDIO 16 +#define BTM_SEC_SERVICE_GEN_TEL 17 +#define BTM_SEC_SERVICE_CTP_DATA 18 +#define BTM_SEC_SERVICE_HCRP_CTRL 19 +#define BTM_SEC_SERVICE_HCRP_DATA 20 +#define BTM_SEC_SERVICE_HCRP_NOTIF 21 +#define BTM_SEC_SERVICE_BPP_JOB 22 +#define BTM_SEC_SERVICE_BPP_STATUS 23 +#define BTM_SEC_SERVICE_BPP_REF 24 +#define BTM_SEC_SERVICE_BNEP_PANU 25 +#define BTM_SEC_SERVICE_BNEP_GN 26 +#define BTM_SEC_SERVICE_BNEP_NAP 27 +#define BTM_SEC_SERVICE_HF_HANDSFREE 28 +#define BTM_SEC_SERVICE_AG_HANDSFREE 29 +#define BTM_SEC_SERVICE_TE_PHONE_ACCESS 30 +#define BTM_SEC_SERVICE_ME_PHONE_ACCESS 31 + +#define BTM_SEC_SERVICE_HID_SEC_CTRL 32 +#define BTM_SEC_SERVICE_HID_NOSEC_CTRL 33 +#define BTM_SEC_SERVICE_HID_INTR 34 +#define BTM_SEC_SERVICE_BIP 35 +#define BTM_SEC_SERVICE_BIP_REF 36 +#define BTM_SEC_SERVICE_AVDTP 37 +#define BTM_SEC_SERVICE_AVDTP_NOSEC 38 +#define BTM_SEC_SERVICE_AVCTP 39 +#define BTM_SEC_SERVICE_SAP 40 +#define BTM_SEC_SERVICE_PBAP 41 +#define BTM_SEC_SERVICE_RFC_MUX 42 +#define BTM_SEC_SERVICE_AVCTP_BROWSE 43 +#define BTM_SEC_SERVICE_MAP 44 +#define BTM_SEC_SERVICE_MAP_NOTIF 45 +#define BTM_SEC_SERVICE_MCAP_CTRL 46 +#define BTM_SEC_SERVICE_MCAP_DATA 47 +#define BTM_SEC_SERVICE_HDP_SNK 48 +#define BTM_SEC_SERVICE_HDP_SRC 49 +#define BTM_SEC_SERVICE_ATT 50 + +/* Update these as services are added */ +#define BTM_SEC_SERVICE_FIRST_EMPTY 51 + +#ifndef BTM_SEC_MAX_SERVICES +#define BTM_SEC_MAX_SERVICES 65 +#endif + +/************************************************************************************************ +** Security Services MACROS handle array of UINT32 bits for more than 32 trusted services +*************************************************************************************************/ +/* MACRO to set the security service bit mask in a bit stream */ +#define BTM_SEC_SET_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)] |= \ + ((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS))) + + +/* MACRO to clear the security service bit mask in a bit stream */ +#define BTM_SEC_CLR_SERVICE(p, service) (((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)] &= \ + ~((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS))) + +/* MACRO to check the security service bit mask in a bit stream (Returns TRUE or FALSE) */ +#define BTM_SEC_IS_SERVICE_TRUSTED(p, service) (((((UINT32 *)(p))[(((UINT32)(service)) / BTM_SEC_ARRAY_BITS)]) & \ + (UINT32)(((UINT32)1 << (((UINT32)(service)) % BTM_SEC_ARRAY_BITS)))) ? TRUE : FALSE) + +/* MACRO to copy two trusted device bitmask */ +#define BTM_SEC_COPY_TRUSTED_DEVICE(p_src, p_dst) {int trst; for (trst = 0; trst < BTM_SEC_SERVICE_ARRAY_SIZE; trst++) \ + ((UINT32 *)(p_dst))[trst] = ((UINT32 *)(p_src))[trst];} + +/* MACRO to clear two trusted device bitmask */ +#define BTM_SEC_CLR_TRUSTED_DEVICE(p_dst) {int trst; for (trst = 0; trst < BTM_SEC_SERVICE_ARRAY_SIZE; trst++) \ + ((UINT32 *)(p_dst))[trst] = 0;} + +/* Following bits can be provided by host in the trusted_mask array */ +/* 0..31 bits of mask[0] (Least Significant Word) */ +#define BTM_SEC_TRUST_SDP_SERVER (1 << BTM_SEC_SERVICE_SDP_SERVER) +#define BTM_SEC_TRUST_SERIAL_PORT (1 << BTM_SEC_SERVICE_SERIAL_PORT) +#define BTM_SEC_TRUST_LAN_ACCESS (1 << BTM_SEC_SERVICE_LAN_ACCESS) +#define BTM_SEC_TRUST_DUN (1 << BTM_SEC_SERVICE_DUN) +#define BTM_SEC_TRUST_IRMC_SYNC (1 << BTM_SEC_SERVICE_IRMC_SYNC) +#define BTM_SEC_TRUST_IRMC_SYNC_CMD (1 << BTM_SEC_SERVICE_IRMC_SYNC_CMD) +#define BTM_SEC_TRUST_OBEX (1 << BTM_SEC_SERVICE_OBEX) +#define BTM_SEC_TRUST_OBEX_FTP (1 << BTM_SEC_SERVICE_OBEX_FTP) +#define BTM_SEC_TRUST_HEADSET (1 << BTM_SEC_SERVICE_HEADSET) +#define BTM_SEC_TRUST_CORDLESS (1 << BTM_SEC_SERVICE_CORDLESS) +#define BTM_SEC_TRUST_INTERCOM (1 << BTM_SEC_SERVICE_INTERCOM) +#define BTM_SEC_TRUST_FAX (1 << BTM_SEC_SERVICE_FAX) +#define BTM_SEC_TRUST_HEADSET_AG (1 << BTM_SEC_SERVICE_HEADSET_AG) +#define BTM_SEC_TRUST_PNP_INFO (1 << BTM_SEC_SERVICE_PNP_INFO) +#define BTM_SEC_TRUST_GEN_NET (1 << BTM_SEC_SERVICE_GEN_NET) +#define BTM_SEC_TRUST_GEN_FILE (1 << BTM_SEC_SERVICE_GEN_FILE) +#define BTM_SEC_TRUST_GEN_AUDIO (1 << BTM_SEC_SERVICE_GEN_AUDIO) +#define BTM_SEC_TRUST_GEN_TEL (1 << BTM_SEC_SERVICE_GEN_TEL) +#define BTM_SEC_TRUST_CTP_DATA (1 << BTM_SEC_SERVICE_CTP_DATA) +#define BTM_SEC_TRUST_HCRP_CTRL (1 << BTM_SEC_SERVICE_HCRP_CTRL) +#define BTM_SEC_TRUST_HCRP_DATA (1 << BTM_SEC_SERVICE_HCRP_DATA) +#define BTM_SEC_TRUST_HCRP_NOTIF (1 << BTM_SEC_SERVICE_HCRP_NOTIF) +#define BTM_SEC_TRUST_BPP_JOB (1 << BTM_SEC_SERVICE_JOB) +#define BTM_SEC_TRUST_BPP_STATUS (1 << BTM_SEC_SERVICE_STATUS) +#define BTM_SEC_TRUST_BPP_REF (1 << BTM_SEC_SERVICE_REF) +#define BTM_SEC_TRUST_BNEP_PANU (1 << BTM_SEC_SERVICE_BNEP_PANU) +#define BTM_SEC_TRUST_BNEP_GN (1 << BTM_SEC_SERVICE_BNEP_GN) +#define BTM_SEC_TRUST_BNEP_NAP (1 << BTM_SEC_SERVICE_BNEP_NAP) +#define BTM_SEC_TRUST_HFP_HF (1 << BTM_SEC_SERVICE_HF_HANDSFREE) +#define BTM_SEC_TRUST_HFP_AG (1 << BTM_SEC_SERVICE_AG_HANDSFREE) +#define BTM_SEC_TRUST_TE_PHONE_ACCESS (1 << BTM_SEC_SERVICE_TE_PHONE_ACCESS) +#define BTM_SEC_TRUST_ME_PHONE_ACCESS (1 << BTM_SEC_SERVICE_ME_PHONE_ACCESS) + +/* 0..31 bits of mask[1] (Most Significant Word) */ +#define BTM_SEC_TRUST_HID_CTRL (1 << (BTM_SEC_SERVICE_HID_SEC_CTRL - 32)) +#define BTM_SEC_TRUST_HID_NOSEC_CTRL (1 << (BTM_SEC_SERVICE_HID_NOSEC_CTRL - 32)) +#define BTM_SEC_TRUST_HID_INTR (1 << (BTM_SEC_SERVICE_HID_INTR - 32)) +#define BTM_SEC_TRUST_BIP (1 << (BTM_SEC_SERVICE_BIP - 32)) +#define BTM_SEC_TRUST_BIP_REF (1 << (BTM_SEC_SERVICE_BIP_REF - 32)) +#define BTM_SEC_TRUST_AVDTP (1 << (BTM_SEC_SERVICE_AVDTP - 32)) +#define BTM_SEC_TRUST_AVDTP_NOSEC (1 << (BTM_SEC_SERVICE_AVDTP_NOSEC - 32)) +#define BTM_SEC_TRUST_AVCTP (1 << (BTM_SEC_SERVICE_AVCTP - 32)) +#define BTM_SEC_TRUST_SAP (1 << (BTM_SEC_SERVICE_SAP - 32)) +#define BTM_SEC_TRUST_PBAP (1 << (BTM_SEC_SERVICE_PBAP - 32)) +#define BTM_SEC_TRUST_RFC_MUX (1 << (BTM_SEC_SERVICE_RFC_MUX - 32)) +#define BTM_SEC_TRUST_AVCTP_BROWSE (1 << (BTM_SEC_SERVICE_AVCTP_BROWSE - 32)) +#define BTM_SEC_TRUST_MAP (1 << (BTM_SEC_SERVICE_MAP - 32)) +#define BTM_SEC_TRUST_MAP_NOTIF (1 << (BTM_SEC_SERVICE_MAP_NOTIF - 32)) +#define BTM_SEC_TRUST_MCAP_CTRL (1 << (BTM_SEC_SERVICE_MCAP_CTRL - 32)) +#define BTM_SEC_TRUST_MCAP_DATA (1 << (BTM_SEC_SERVICE_MCAP_DATA - 32)) +#define BTM_SEC_TRUST_HDP_SNK (1 << (BTM_SEC_SERVICE_HDP_SNK - 32)) +#define BTM_SEC_TRUST_HDP_SRC (1 << (BTM_SEC_SERVICE_HDP_SRC - 32)) + +#define BTM_SEC_TRUST_ALL 0xFFFFFFFF /* for each array element */ + +/**************************************** +** Security Manager Callback Functions +*****************************************/ +/* Authorize device for service. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +** Service name +** Service Id (NULL - unknown service or unused +** [BTM_SEC_SERVICE_NAME_LEN set to 0]) +** Is originator of the connection +** Result of the operation +*/ +typedef UINT8 (tBTM_AUTHORIZE_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, UINT8 *service_name, + UINT8 service_id, BOOLEAN is_originator); + +/* Get PIN for the connection. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +*/ +typedef UINT8 (tBTM_PIN_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name); + + +/* Get Link Key for the connection. Parameters are +** BD Address of remote +** Link Key +*/ +typedef UINT8 (tBTM_LINK_KEY_REQ_CALLBACK) (BD_ADDR bd_addr, LINK_KEY key); + +/* New Link Key for the connection. Parameters are +** BD Address of remote +** Link Key +** Key Type: Combination, Local Unit, or Remote Unit +*/ +typedef UINT8 (tBTM_LINK_KEY_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, UINT8 *key, + UINT8 key_type); + + +/* Remote Name Resolved. Parameters are +** BD Address of remote +** BD Name of remote +*/ +typedef void (tBTM_RMT_NAME_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dc, + tBTM_BD_NAME bd_name); + + +/* Authentication complete for the connection. Parameters are +** BD Address of remote +** Device Class of remote +** BD Name of remote +** +*/ +typedef UINT8 (tBTM_AUTH_COMPLETE_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name, int result); + +/* Operation abort. Called by the stack when link goes down during. Pin code +** request or authorization. Parameters are +** BD Address of remote +** +*/ +typedef UINT8 (tBTM_ABORT_CALLBACK) (BD_ADDR bd_addr, DEV_CLASS dev_class, + tBTM_BD_NAME bd_name); + +enum +{ + BTM_SP_IO_REQ_EVT, /* received IO_CAPABILITY_REQUEST event */ + BTM_SP_IO_RSP_EVT, /* received IO_CAPABILITY_RESPONSE event */ + BTM_SP_CFM_REQ_EVT, /* received USER_CONFIRMATION_REQUEST event */ + BTM_SP_KEY_NOTIF_EVT, /* received USER_PASSKEY_NOTIFY event */ + BTM_SP_KEY_REQ_EVT, /* received USER_PASSKEY_REQUEST event */ + BTM_SP_KEYPRESS_EVT, /* received KEYPRESS_NOTIFY event */ + BTM_SP_LOC_OOB_EVT, /* received result for READ_LOCAL_OOB_DATA command */ + BTM_SP_RMT_OOB_EVT, /* received REMOTE_OOB_DATA_REQUEST event */ + BTM_SP_COMPLT_EVT, /* received SIMPLE_PAIRING_COMPLETE event */ + BTM_SP_UPGRADE_EVT /* check if the application wants to upgrade the link key */ +}; +typedef UINT8 tBTM_SP_EVT; + +#define BTM_IO_CAP_OUT 0 /* DisplayOnly */ +#define BTM_IO_CAP_IO 1 /* DisplayYesNo */ +#define BTM_IO_CAP_IN 2 /* KeyboardOnly */ +#define BTM_IO_CAP_NONE 3 /* NoInputNoOutput */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#define BTM_IO_CAP_KBDISP 4 /* Keyboard display */ +#define BTM_IO_CAP_MAX 5 +#else +#define BTM_IO_CAP_MAX 4 +#endif + +typedef UINT8 tBTM_IO_CAP; + +#define BTM_MAX_PASSKEY_VAL (999999) +#define BTM_MIN_PASSKEY_VAL (0) + +#define BTM_AUTH_SP_NO 0 /* MITM Protection Not Required - Single Profile/non-bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_SP_YES 1 /* MITM Protection Required - Single Profile/non-bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_AP_NO 2 /* MITM Protection Not Required - All Profiles/dedicated bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_AP_YES 3 /* MITM Protection Required - All Profiles/dedicated bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_SPGB_NO 4 /* MITM Protection Not Required - Single Profiles/general bonding + Numeric comparison with automatic accept allowed */ +#define BTM_AUTH_SPGB_YES 5 /* MITM Protection Required - Single Profiles/general bonding + Use IO Capabilities to determine authentication procedure */ +#define BTM_AUTH_DD_BOND 2 /* this bit is ORed to the BTM_AUTH_SP_* when IO exchange for dedicated bonding */ +#define BTM_AUTH_GB_BIT 4 /* the genernal bonding bit */ +#define BTM_AUTH_BONDS 6 /* the general/dedicated bonding bits */ +#define BTM_AUTH_YN_BIT 1 /* this is the Yes or No bit */ + +typedef UINT8 tBTM_AUTH_REQ; + +enum +{ + BTM_OOB_NONE, + BTM_OOB_PRESENT +#if BTM_OOB_INCLUDED == TRUE + ,BTM_OOB_UNKNOWN +#endif +}; +typedef UINT8 tBTM_OOB_DATA; + +/* data type for BTM_SP_IO_REQ_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + tBTM_IO_CAP io_cap; /* local IO capabilities */ + tBTM_OOB_DATA oob_data; /* OOB data present (locally) for the peer device */ + tBTM_AUTH_REQ auth_req; /* Authentication required (for local device) */ + BOOLEAN is_orig; /* TRUE, if local device initiated the SP process */ +} tBTM_SP_IO_REQ; + +/* data type for BTM_SP_IO_RSP_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + tBTM_IO_CAP io_cap; /* peer IO capabilities */ + tBTM_OOB_DATA oob_data; /* OOB data present at peer device for the local device */ + tBTM_AUTH_REQ auth_req; /* Authentication required for peer device */ +} tBTM_SP_IO_RSP; + +/* data type for BTM_SP_CFM_REQ_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + UINT32 num_val; /* the numeric value for comparison. If just_works, do not show this number to UI */ + BOOLEAN just_works; /* TRUE, if "Just Works" association model */ + tBTM_AUTH_REQ loc_auth_req; /* Authentication required for local device */ + tBTM_AUTH_REQ rmt_auth_req; /* Authentication required for peer device */ + tBTM_IO_CAP loc_io_caps; /* IO Capabilities of the local device */ + tBTM_IO_CAP rmt_io_caps; /* IO Capabilities of the remot device */ +} tBTM_SP_CFM_REQ; + +/* data type for BTM_SP_KEY_REQ_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ +} tBTM_SP_KEY_REQ; + +/* data type for BTM_SP_KEY_NOTIF_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + UINT32 passkey; /* passkey */ +} tBTM_SP_KEY_NOTIF; + +enum +{ + BTM_SP_KEY_STARTED, /* passkey entry started */ + BTM_SP_KEY_ENTERED, /* passkey digit entered */ + BTM_SP_KEY_ERASED, /* passkey digit erased */ + BTM_SP_KEY_CLEARED, /* passkey cleared */ + BTM_SP_KEY_COMPLT /* passkey entry completed */ +}; +typedef UINT8 tBTM_SP_KEY_TYPE; + +/* data type for BTM_SP_KEYPRESS_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + tBTM_SP_KEY_TYPE notif_type; +} tBTM_SP_KEYPRESS; + +/* data type for BTM_SP_LOC_OOB_EVT */ +typedef struct +{ + tBTM_STATUS status; /* */ + BT_OCTET16 c; /* Simple Pairing Hash C */ + BT_OCTET16 r; /* Simple Pairing Randomnizer R */ +} tBTM_SP_LOC_OOB; + +/* data type for BTM_SP_RMT_OOB_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ +} tBTM_SP_RMT_OOB; + + +/* data type for BTM_SP_COMPLT_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + DEV_CLASS dev_class; /* peer CoD */ + tBTM_BD_NAME bd_name; /* peer device name */ + tBTM_STATUS status; /* status of the simple pairing process */ +} tBTM_SP_COMPLT; + +/* data type for BTM_SP_UPGRADE_EVT */ +typedef struct +{ + BD_ADDR bd_addr; /* peer address */ + BOOLEAN upgrade; /* TRUE, to upgrade the link key */ +} tBTM_SP_UPGRADE; + +typedef union +{ + tBTM_SP_IO_REQ io_req; /* BTM_SP_IO_REQ_EVT */ + tBTM_SP_IO_RSP io_rsp; /* BTM_SP_IO_RSP_EVT */ + tBTM_SP_CFM_REQ cfm_req; /* BTM_SP_CFM_REQ_EVT */ + tBTM_SP_KEY_NOTIF key_notif; /* BTM_SP_KEY_NOTIF_EVT */ + tBTM_SP_KEY_REQ key_req; /* BTM_SP_KEY_REQ_EVT */ + tBTM_SP_KEYPRESS key_press; /* BTM_SP_KEYPRESS_EVT */ + tBTM_SP_LOC_OOB loc_oob; /* BTM_SP_LOC_OOB_EVT */ + tBTM_SP_RMT_OOB rmt_oob; /* BTM_SP_RMT_OOB_EVT */ + tBTM_SP_COMPLT complt; /* BTM_SP_COMPLT_EVT */ + tBTM_SP_UPGRADE upgrade; /* BTM_SP_UPGRADE_EVT */ +} tBTM_SP_EVT_DATA; + +/* Simple Pairing Events. Called by the stack when Simple Pairing related +** events occur. +*/ +typedef UINT8 (tBTM_SP_CALLBACK) (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data); + + +typedef void (tBTM_MKEY_CALLBACK) (BD_ADDR bd_addr, UINT8 status, UINT8 key_flag) ; + +/* Encryption enabled/disabled complete: Optionally passed with BTM_SetEncryption. +** Parameters are +** BD Address of remote +** optional data passed in by BTM_SetEncryption +** tBTM_STATUS - result of the operation +*/ +typedef void (tBTM_SEC_CBACK) (BD_ADDR bd_addr, void *p_ref_data, tBTM_STATUS result); + +/* Bond Cancel complete. Parameters are +** Result of the cancel operation +** +*/ +typedef void (tBTM_BOND_CANCEL_CMPL_CALLBACK) (tBTM_STATUS result); + +/* LE related event and data structure +*/ +enum +{ + BTM_LE_IO_REQ_EVT = 1, /* received IO_CAPABILITY_REQUEST event */ + BTM_LE_SEC_REQUEST_EVT, /* security request event */ + BTM_LE_KEY_NOTIF_EVT, /* received USER_PASSKEY_NOTIFY event */ + BTM_LE_KEY_REQ_EVT, /* received USER_PASSKEY_REQUEST event */ + BTM_LE_OOB_REQ_EVT, /* OOB data request event */ + BTM_LE_COMPLT_EVT, /* received SIMPLE_PAIRING_COMPLETE event */ + BTM_LE_KEY_EVT /* KEY update event */ +}; +typedef UINT8 tBTM_LE_EVT; + +#define BTM_LE_KEY_PENC SMP_SEC_KEY_TYPE_ENC /* encryption information of peer device */ +#define BTM_LE_KEY_PID SMP_SEC_KEY_TYPE_ID /* identity key of the peer device */ +#define BTM_LE_KEY_PCSRK SMP_SEC_KEY_TYPE_CSRK /* peer SRK */ +#define BTM_LE_KEY_LENC (SMP_SEC_KEY_TYPE_ENC << 3) /* master role security information:div */ +#define BTM_LE_KEY_LID (SMP_SEC_KEY_TYPE_ID << 3) /* master device ID key */ +#define BTM_LE_KEY_LCSRK (SMP_SEC_KEY_TYPE_CSRK << 3) /* local CSRK has been deliver to peer */ +typedef UINT8 tBTM_LE_KEY_TYPE; + +#define BTM_LE_AUTH_REQ_NO_BOND SMP_AUTH_NO_BOND /* 0 */ +#define BTM_LE_AUTH_REQ_BOND SMP_AUTH_GEN_BOND /* 1 << 0 */ +#define BTM_LE_AUTH_REQ_MITM SMP_AUTH_YN_BIT /* 1 << 2 */ +typedef UINT8 tBTM_LE_AUTH_REQ; + +#define BTM_LE_AUTH_REQ_MASK SMP_AUTH_MASK /* 0x03*/ + +/* LE security level */ +#define BTM_LE_SEC_NONE SMP_SEC_NONE +#define BTM_LE_SEC_UNAUTHENTICATE SMP_SEC_UNAUTHENTICATE +#define BTM_LE_SEC_AUTHENTICATED SMP_SEC_AUTHENTICATED +typedef UINT8 tBTM_LE_SEC; + + +typedef struct +{ + tBTM_IO_CAP io_cap; /* local IO capabilities */ + UINT8 oob_data; /* OOB data present (locally) for the peer device */ + tBTM_LE_AUTH_REQ auth_req; /* Authentication request (for local device) contain bonding and MITM info */ + UINT8 max_key_size; /* max encryption key size */ + tBTM_LE_KEY_TYPE init_keys; /* keys to be distributed, bit mask */ + tBTM_LE_KEY_TYPE resp_keys; /* keys to be distributed, bit mask */ +} tBTM_LE_IO_REQ; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +/* data type for tBTM_LE_COMPLT */ +typedef struct +{ + UINT8 reason; + UINT8 sec_level; +}tBTM_LE_COMPLT; +#endif + +/* BLE encryption keys */ +typedef struct +{ + BT_OCTET16 ltk; + BT_OCTET8 rand; + UINT16 ediv; + UINT8 sec_level; + UINT8 key_size; +}tBTM_LE_PENC_KEYS; + +/* BLE CSRK keys */ +typedef struct +{ + UINT32 counter; + BT_OCTET16 csrk; + UINT8 sec_level; +}tBTM_LE_PCSRK_KEYS; + +/* BLE Encryption reproduction keys */ +typedef struct +{ + UINT16 div; + UINT8 key_size; + UINT8 sec_level; +}tBTM_LE_LENC_KEYS; + +/* BLE SRK keys */ +typedef struct +{ + UINT32 counter; + UINT16 div; + UINT8 sec_level; + +}tBTM_LE_LCSRK_KEYS; + + +typedef union +{ + tBTM_LE_PENC_KEYS penc_key; /* received peer encryption key */ + tBTM_LE_PCSRK_KEYS pcsrk_key; /* received peer device SRK */ + BT_OCTET16 pid_key; /* peer device ID key */ + tBTM_LE_LENC_KEYS lenc_key; /* local encryption reproduction keys LTK = = d1(ER,DIV,0)*/ + tBTM_LE_LCSRK_KEYS lcsrk_key; /* local device CSRK = d1(ER,DIV,1)*/ +}tBTM_LE_KEY_VALUE; + +typedef struct +{ + tBTM_LE_KEY_TYPE key_type; + tBTM_LE_KEY_VALUE *p_key_value; +}tBTM_LE_KEY; + +typedef union +{ + tBTM_LE_IO_REQ io_req; /* BTM_LE_IO_REQ_EVT */ + UINT32 key_notif; /* BTM_LE_KEY_NOTIF_EVT */ + /* no callback dta for BTM_LE_KEY_REQ_EVT & BTM_LE_OOB_REQ_EVT */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tBTM_LE_COMPLT complt; /* BTM_LE_COMPLT_EVT */ +#endif + tBTM_LE_KEY key; +} tBTM_LE_EVT_DATA; + +/* Simple Pairing Events. Called by the stack when Simple Pairing related +** events occur. +*/ +typedef UINT8 (tBTM_LE_CALLBACK) (tBTM_LE_EVT event, BD_ADDR bda, tBTM_LE_EVT_DATA *p_data); + +#define BTM_BLE_KEY_TYPE_ID 1 +#define BTM_BLE_KEY_TYPE_ER 2 +#define BTM_BLE_KEY_TYPE_COUNTER 3 //tobe obsolete + +typedef struct +{ + BT_OCTET16 ir; + BT_OCTET16 irk; + BT_OCTET16 dhk; + +}tBTM_BLE_LOCAL_ID_KEYS; + +typedef union +{ + tBTM_BLE_LOCAL_ID_KEYS id_keys; + BT_OCTET16 er; +}tBTM_BLE_LOCAL_KEYS; + + +/* New LE identity key for local device. +*/ +typedef void (tBTM_LE_KEY_CALLBACK) (UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key); + + +/*************************** +** Security Manager Types +****************************/ +/* Structure that applications use to register with BTM_SecRegister */ +typedef struct +{ + tBTM_AUTHORIZE_CALLBACK *p_authorize_callback; + tBTM_PIN_CALLBACK *p_pin_callback; + tBTM_LINK_KEY_CALLBACK *p_link_key_callback; + tBTM_LINK_KEY_REQ_CALLBACK *p_link_key_req_callback; + tBTM_AUTH_COMPLETE_CALLBACK *p_auth_complete_callback; + tBTM_ABORT_CALLBACK *p_abort_callback; + tBTM_BOND_CANCEL_CMPL_CALLBACK *p_bond_cancel_cmpl_callback; + tBTM_SP_CALLBACK *p_sp_callback; +#if BLE_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE + tBTM_LE_CALLBACK *p_le_callback; +#endif + tBTM_LE_KEY_CALLBACK *p_le_key_callback; +#endif +} tBTM_APPL_INFO; + +/* Callback function for when a link supervision timeout event occurs. +** This asynchronous event is enabled/disabled by calling BTM_RegForLstoEvt(). +*/ +typedef void (tBTM_LSTO_CBACK) (BD_ADDR remote_bda, UINT16 timeout); + +/***************************************************************************** +** POWER MANAGEMENT +*****************************************************************************/ +/**************************** +** Power Manager Constants +*****************************/ +/* BTM Power manager status codes */ +enum +{ + BTM_PM_STS_ACTIVE = HCI_MODE_ACTIVE, + BTM_PM_STS_HOLD = HCI_MODE_HOLD, + BTM_PM_STS_SNIFF = HCI_MODE_SNIFF, + BTM_PM_STS_PARK = HCI_MODE_PARK, + BTM_PM_STS_SSR, /* report the SSR parameters in HCI_SNIFF_SUB_RATE_EVT */ + BTM_PM_STS_PENDING, /* when waiting for status from controller */ + BTM_PM_STS_ERROR /* when HCI command status returns error */ +}; +typedef UINT8 tBTM_PM_STATUS; + +/* BTM Power manager modes */ +enum +{ + BTM_PM_MD_ACTIVE = BTM_PM_STS_ACTIVE, + BTM_PM_MD_HOLD = BTM_PM_STS_HOLD, + BTM_PM_MD_SNIFF = BTM_PM_STS_SNIFF, + BTM_PM_MD_PARK = BTM_PM_STS_PARK, + BTM_PM_MD_FORCE = 0x10 /* OR this to force ACL link to a certain mode */ +}; +typedef UINT8 tBTM_PM_MODE; + +#define BTM_PM_SET_ONLY_ID 0x80 + +/* Operation codes */ +#define BTM_PM_REG_SET 1 /* The module wants to set the desired power mode */ +#define BTM_PM_REG_NOTIF 2 /* The module wants to receive mode change event */ +#define BTM_PM_DEREG 4 /* The module does not want to involve with PM anymore */ + +/************************ +** Power Manager Types +*************************/ +typedef struct +{ + UINT16 max; + UINT16 min; + UINT16 attempt; + UINT16 timeout; + tBTM_PM_MODE mode; +} tBTM_PM_PWR_MD; + +/************************************* +** Power Manager Callback Functions +**************************************/ +typedef void (tBTM_PM_STATUS_CBACK) (BD_ADDR p_bda, tBTM_PM_STATUS status, + UINT16 value, UINT8 hci_status); + + +/************************ +** Stored Linkkey Types +*************************/ +#define BTM_CB_EVT_RETURN_LINK_KEYS 1 +#define BTM_CB_EVT_READ_STORED_LINK_KEYS 2 +#define BTM_CB_EVT_WRITE_STORED_LINK_KEYS 3 +#define BTM_CB_EVT_DELETE_STORED_LINK_KEYS 4 + +typedef struct +{ + UINT8 event; + +} tBTM_STORED_LINK_KEYS_EVT; + + +typedef struct +{ + UINT8 event; + UINT8 num_keys; + +} tBTM_RETURN_LINK_KEYS_EVT; + + +typedef struct +{ + BD_ADDR bd_addr; + LINK_KEY link_key; + +} tBTM_BD_ADDR_LINK_KEY_PAIR; + + +typedef struct +{ + UINT8 event; + UINT8 status; + UINT16 max_keys; + UINT16 read_keys; + +} tBTM_READ_STORED_LINK_KEY_COMPLETE; + + +typedef struct +{ + UINT8 event; + UINT8 status; + UINT8 num_keys; + +} tBTM_WRITE_STORED_LINK_KEY_COMPLETE; + + +typedef struct +{ + UINT8 event; + UINT8 status; + UINT16 num_keys; + +} tBTM_DELETE_STORED_LINK_KEY_COMPLETE; + + +/* These macros are defined to check the Broadcom features supported in controller + * (the return value for BTM_ReadBrcmFeatures() */ +/* multi-av */ +#define BTM_FEATURE_MULTI_AV_MASK 0x01 +#define BTM_FEATURE_MULTI_AV_OFF 0 +#define BTM_VSC_MULTI_AV_SUPPORTED(x) ((x)[BTM_FEATURE_MULTI_AV_OFF] & BTM_FEATURE_MULTI_AV_MASK) + +/* WBS SBC codec */ +#define BTM_FEATURE_WBS_SBC_MASK 0x02 +#define BTM_FEATURE_WBS_SBC_OFF 0 +#define BTM_VSC_WBS_SBC_SUPPORTED(x) ((x)[BTM_FEATURE_WBS_SBC_OFF] & BTM_FEATURE_WBS_SBC_MASK) + +/* Advanced Audio LC-PLC */ +#define BTM_FEATURE_AUDIO_LC_PLC_MASK 0x04 +#define BTM_FEATURE_AUDIO_LC_PLC_OFF 0 +#define BTM_VSC_AUDIO_LC_PLC_SUPPORTED(x) ((x)[BTM_FEATURE_AUDIO_LC_PLC_OFF] & BTM_FEATURE_AUDIO_LC_PLC_MASK) + +/* Light stack for audio routing in Controller */ +#define BTM_FEATURE_LIGHT_STACK_MASK 0x08 +#define BTM_FEATURE_LIGHT_STACK_OFF 0 +#define BTM_VSC_LIGHT_STACK_SUPPORTED(x) ((x)[BTM_FEATURE_LIGHT_STACK_OFF] & BTM_FEATURE_LIGHT_STACK_MASK) + +/* NFC support */ +#define BTM_FEATURE_NFC_MASK (HCI_BRCM_FEATURE_NFC_MASK) /* 0x10 */ +#define BTM_FEATURE_NFC_OFF (HCI_BRCM_FEATURE_NFC_OFF) /* 0 */ +#define BTM_VSC_NFC_SUPPORTED(x) ((x)[BTM_FEATURE_NFC_OFF] & BTM_FEATURE_NFC_MASK) + + +/************************ +** Dual-Stack support +*************************/ +/* BTM_SYNC_FAIL_EVT reason codes */ +#define BTM_SYNC_SUCCESS 0 +#define BTM_SYNC_FAIL_BTE_SWITCH_REJECTED 1 +#define BTM_SYNC_FAIL_TRANS_PAUSE 2 +#define BTM_SYNC_FAIL_CORE_SYNC 3 +#define BTM_SYNC_FAIL_BTA_SYNC 4 +#define BTM_SYNC_FAIL_TRANS_RESUME 5 +#define BTM_SYNC_FAIL_RESYNC 6 +#define BTM_SYNC_FAIL_ERROR 7 +#define BTM_SYNC_FAIL_UIPC_OPEN 8 +typedef UINT8 tBTM_SYNC_STATUS; + +/* Direction of sync (used by BTM_SyncStack) */ +#define BTM_SW_BB_TO_MM 0 +#define BTM_SW_TO_BB 1 /* Switch back to baseband stack (from either MM or BTC host) */ +#define BTM_SW_RESYNC 2 +#define BTM_SW_BB_TO_BTC 3 /* Switch from baseband stack to Bluetooth Controller Host stack */ +#define BTM_SW_MM_TO_BB 4 +#define BTM_SW_BTC_TO_BB 5 +typedef UINT8 tBTM_SW_DIR; + +/* Stack synchronization events (returned by tBTM_SYNC_STACK_CBACK callback) */ +#define BTM_SYNC_CPLT_EVT 0 +#define BTM_SYNC_BTA_EVT 1 +#define BTM_RESYNC_CPLT_EVT 2 +#define BTM_UIPC_OPENED_EVT 3 +#define BTM_UIPC_CLOSED_EVT 4 +typedef UINT8 tBTM_SYNC_STACK_EVT; + +/* Synchronization info from BTA/application that will be sent when calling BTE sync request functions */ +typedef struct +{ + tBTM_SW_DIR dir; + UINT16 lcid[BTM_SYNC_INFO_NUM_STR]; + UINT8 avdt_handle[BTM_SYNC_INFO_NUM_STR]; +} tBTM_SYNC_INFO; + +/* Stack synchonization callback function +** Parameters are +** event: stack synchronization event +** status: BTM_SUCCESS if event was successful +*/ +typedef void (*tBTM_SYNC_STACK_CBACK)(tBTM_SYNC_STACK_EVT event, tBTM_SYNC_STATUS status); + + +/* Sync complete callback function. Called by bte layers after synchronization is complete +** so that BTM_SYNC can procede with the next step for switching stack to MM +** +** Parameters are +** status: BTM_SUCCESS if synchronization was successful +*/ +typedef void (*tBTM_SYNC_CPLT_CBACK)(tBTM_STATUS status); + + + +/* IPC event callback function. Called by BTM when an IPC event is received. +** These events are currently sent to DM through the callback function. +** +** Parameters are +** status: BTM_SUCCESS if synchronization was successful +** p_data: Actual message in the IPC +*/ +typedef void (tBTM_IPC_EVT_CBACK)(tBTM_STATUS status, BT_HDR *p_data); + +/* MIP evnets, callbacks */ +enum +{ + BTM_MIP_MODE_CHG_EVT, + BTM_MIP_DISCONNECT_EVT, + BTM_MIP_PKTS_COMPL_EVT, + BTM_MIP_RXDATA_EVT +}; +typedef UINT8 tBTM_MIP_EVT; + +typedef struct +{ + tBTM_MIP_EVT event; + BD_ADDR bd_addr; + UINT16 mip_id; +} tBTM_MIP_MODE_CHANGE; + +typedef struct +{ + tBTM_MIP_EVT event; + UINT16 mip_id; + UINT8 disc_reason; +} tBTM_MIP_CONN_TIMEOUT; + +#define BTM_MIP_MAX_RX_LEN 17 + +typedef struct +{ + tBTM_MIP_EVT event; + UINT16 mip_id; + UINT8 rx_len; + UINT8 rx_data[BTM_MIP_MAX_RX_LEN]; +} tBTM_MIP_RXDATA; + +typedef struct +{ + tBTM_MIP_EVT event; + BD_ADDR bd_addr; + UINT8 data[11]; /* data[0] shows Vender-specific device type */ +} tBTM_MIP_EIR_HANDSHAKE; + +typedef struct +{ + tBTM_MIP_EVT event; + UINT16 num_sent; /* Number of packets completed at the controller */ +} tBTM_MIP_PKTS_COMPL; + +typedef union +{ + tBTM_MIP_EVT event; + tBTM_MIP_MODE_CHANGE mod_chg; + tBTM_MIP_CONN_TIMEOUT conn_tmo; + tBTM_MIP_EIR_HANDSHAKE eir; + tBTM_MIP_PKTS_COMPL completed; + tBTM_MIP_RXDATA rxdata; +} tBTM_MIP_EVENT_DATA; + +/* MIP event callback function */ +typedef void (tBTM_MIP_EVENTS_CB) (tBTM_MIP_EVT event, tBTM_MIP_EVENT_DATA data); + +/* MIP Device query callback function */ +typedef BOOLEAN (tBTM_MIP_QUERY_CB) (BD_ADDR dev_addr, UINT8 *p_mode, LINK_KEY link_key); + +/***************************************************************************** +** EXTERNAL FUNCTION DECLARATIONS +*****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************** +** DEVICE CONTROL and COMMON FUNCTIONS +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called to disable channels +** +** Returns status +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetAfhChannels (UINT8 first, UINT8 last); + +/******************************************************************************* +** +** Function BTM_SetAfhChannelAssessment +** +** Description This function is called to set the channel assessment mode on or off +** +** Returns status +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetAfhChannelAssessment (BOOLEAN enable_or_disable); + +/******************************************************************************* +** +** Function BTM_DeviceReset +** +** Description This function is called to reset the controller.The Callback function +** if provided is called when startup of the device has +** completed. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_DeviceReset (tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_IsDeviceUp +** +** Description This function is called to check if the device is up. +** +** Returns TRUE if device is up, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_IsDeviceUp (void); + + +/******************************************************************************* +** +** Function BTM_SetLocalDeviceName +** +** Description This function is called to set the local device name. +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetLocalDeviceName (char *p_name); + +/******************************************************************************* +** +** Function BTM_SetDeviceClass +** +** Description This function is called to set the local device class +** +** Returns BTM_SUCCESS if successful, otherwise an error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetDeviceClass (DEV_CLASS dev_class); + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceName +** +** Description This function is called to read the local device name. +** +** Returns status of the operation +** If success, BTM_SUCCESS is returned and p_name points stored +** local device name +** If BTM doesn't store local device name, BTM_NO_RESOURCES is +** is returned and p_name is set to NULL +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLocalDeviceName (char **p_name); + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceNameFromController +** +** Description Get local device name from controller. Do not use cached +** name (used to get chip-id prior to btm reset complete). +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLocalDeviceNameFromController (tBTM_CMPL_CB *p_rln_cmpl_cback); + +/******************************************************************************* +** +** Function BTM_ReadLocalVersion +** +** Description This function is called to read the local device version +** +** Returns BTM_SUCCESS if successful, otherwise an error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLocalVersion (tBTM_VERSION_INFO *p_vers); + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns BTM_SUCCESS +** Callback returns the local device address +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLocalDeviceAddr (tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_GetLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns void +** the local device address is copied into bd_addr +** +*******************************************************************************/ + BTM_API extern void BTM_GetLocalDeviceAddr (BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function BTM_ReadDeviceClass +** +** Description This function is called to read the local device class +** +** Returns pointer to the device class +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_ReadDeviceClass (void); + + +/******************************************************************************* +** +** Function BTM_ReadLocalFeatures +** +** Description This function is called to read the local features +** +** Returns pointer to the local features string +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_ReadLocalFeatures (void); + +/******************************************************************************* +** +** Function BTM_ReadBrcmFeatures +** +** Description This function is called to read the Broadcom specific features +** +** Returns pointer to the Broadcom features string +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_ReadBrcmFeatures (void); + +/******************************************************************************* +** +** Function BTM_RegisterForDeviceStatusNotif +** +** Description This function is called to register for device status +** change notifications. +** +** Returns pointer to previous caller's callback function or NULL if first +** registration. +** +*******************************************************************************/ + BTM_API extern tBTM_DEV_STATUS_CB *BTM_RegisterForDeviceStatusNotif (tBTM_DEV_STATUS_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_RegisterForVSEvents +** +** Description This function is called to register/deregister for vendor +** specific HCI events. +** +** If is_register=TRUE, then the function will be registered; +** if is_register=FALSE, then the function will be deregistered. +** +** Returns BTM_SUCCESS if successful, +** BTM_BUSY if maximum number of callbacks have already been +** registered. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_RegisterForVSEvents (tBTM_VS_EVT_CB *p_cb, BOOLEAN is_register); + + +/******************************************************************************* +** +** Function BTM_ContinueReset +** +** Description Instructs stack to continue its stack initialization after +** an application has completed any vender specific commands +** sent to the controller. +** +** Note: This function is only called if an application +** initialization function has been inserted in the reset +** sequence. (BTM_APP_DEV_INIT is defined with a function). +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_ContinueReset (void); + + +/******************************************************************************* +** +** Function BTM_VendorSpecificCommand +** +** Description Send a vendor specific HCI command to the controller. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_CMD_STARTED Command sent. Waiting for command cmpl event. +** BTM_BUSY Command not sent. Waiting for cmd cmpl event for +** prior command. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, + UINT8 param_len, + UINT8 *p_param_buf, + tBTM_VSC_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_AllocateSCN +** +** Description Look through the Server Channel Numbers for a free one to be +** used with an RFCOMM connection. +** +** Returns Allocated SCN number or 0 if none. +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_AllocateSCN(void); + +// btla-specific ++ +/******************************************************************************* +** +** Function BTM_TryAllocateSCN +** +** Description Try to allocate a fixed server channel +** +** Returns Returns TRUE if server channel was available +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_TryAllocateSCN(UINT8 scn); +// btla-specific -- + + +/******************************************************************************* +** +** Function BTM_FreeSCN +** +** Description Free the specified SCN. +** +** Returns TRUE if successful, FALSE if SCN is not in use or invalid +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_FreeSCN(UINT8 scn); + + +/******************************************************************************* +** +** Function BTM_SetTraceLevel +** +** Description This function sets the trace level for BTM. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_SetTraceLevel (UINT8 new_level); + + +/******************************************************************************* +** +** Function BTM_WritePageTimeout +** +** Description Send HCI Wite Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WritePageTimeout(UINT16 timeout); + +/******************************************************************************* +** +** Function BTM_WriteVoiceSettings +** +** Description Send HCI Write Voice Settings command. +** See hcidefs.h for settings bitmask values. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WriteVoiceSettings(UINT16 settings); + +/******************************************************************************* +** +** Function BTM_EnableTestMode +** +** Description Send HCI the enable device under test command. +** +** Note: Controller can only be taken out of this mode by +** resetting the controller. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_EnableTestMode(void); + + +/***************************************************************************** +** DEVICE DISCOVERY FUNCTIONS - Inquiry, Remote Name, Discovery, Class of Device +*****************************************************************************/ + +/******************************************************************************* +** +** Function BTM_SetDiscoverability +** +** Description This function is called to set the device into or out of +** discoverable mode. Discoverable mode means inquiry +** scans are enabled. If a value of '0' is entered for window or +** interval, the default values are used. +** +** Returns BTM_SUCCESS if successful +** BTM_BUSY if a setting of the filter is already in progress +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetDiscoverability (UINT16 inq_mode, UINT16 window, + UINT16 interval); + + +/******************************************************************************* +** +** Function BTM_ReadDiscoverability +** +** Description This function is called to read the current discoverability +** mode of the device. +** +** Output Params: p_window - current inquiry scan duration +** p_interval - current inquiry scan interval +** +** Returns BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or +** BTM_GENERAL_DISCOVERABLE +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadDiscoverability (UINT16 *p_window, + UINT16 *p_interval); + + +/******************************************************************************* +** +** Function BTM_SetPeriodicInquiryMode +** +** Description This function is called to set the device periodic inquiry mode. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** max_delay - maximum amount of time between successive inquiries +** min_delay - minimum amount of time between successive inquiries +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetPeriodicInquiryMode (tBTM_INQ_PARMS *p_inqparms, + UINT16 max_delay, UINT16 min_delay, + tBTM_INQ_RESULTS_CB *p_results_cb); + + +/******************************************************************************* +** +** Function BTM_StartInquiry +** +** Description This function is called to start an inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** p_results_cb - Pointer to the callback routine which gets called +** upon receipt of an inquiry result. If this field is +** NULL, the application is not notified. +** +** p_cmpl_cb - Pointer to the callback routine which gets called +** upon completion. If this field is NULL, the +** application is not notified when completed. +** Returns tBTM_STATUS +** BTM_CMD_STARTED if successfully initiated +** BTM_BUSY if already in progress +** BTM_ILLEGAL_VALUE if parameter(s) are out of range +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, + tBTM_INQ_RESULTS_CB *p_results_cb, + tBTM_CMPL_CB *p_cmpl_cb); + + +/******************************************************************************* +** +** Function BTM_IsInquiryActive +** +** Description This function returns a bit mask of the current inquiry state +** +** Returns BTM_INQUIRY_INACTIVE if inactive (0) +** BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active +** BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active +** BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_IsInquiryActive (void); + + +/******************************************************************************* +** +** Function BTM_CancelInquiry +** +** Description This function cancels an inquiry if active +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CancelInquiry(void); + + +/******************************************************************************* +** +** Function BTM_CancelPeriodicInquiry +** +** Description This function cancels a periodic inquiry +** +** Returns +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CancelPeriodicInquiry(void); + + +/******************************************************************************* +** +** Function BTM_SetInquiryFilterCallback +** +** Description Host can register to be asked whenever an inquiry result +** is received. If host does not like the device no name +** request is issued for the device +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetInquiryFilterCallback(tBTM_FILTER_CB *p_callback); + + +/******************************************************************************* +** +** Function BTM_SetConnectability +** +** Description This function is called to set the device into or out of +** connectable mode. Discoverable mode means page scans enabled. +** +** Returns BTM_SUCCESS if successful +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetConnectability (UINT16 page_mode, UINT16 window, + UINT16 interval); + + +/******************************************************************************* +** +** Function BTM_ReadConnectability +** +** Description This function is called to read the current discoverability +** mode of the device. +** Output Params p_window - current page scan duration +** p_interval - current time between page scans +** +** Returns BTM_NON_CONNECTABLE or BTM_CONNECTABLE +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadConnectability (UINT16 *p_window, UINT16 *p_interval); + + +/******************************************************************************* +** +** Function BTM_SetInquiryMode +** +** Description This function is called to set standard, with RSSI +** mode or extended of the inquiry for local device. +** +** Input Params: BTM_INQ_RESULT_STANDARD, BTM_INQ_RESULT_WITH_RSSI or +** BTM_INQ_RESULT_EXTENDED +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BT_API extern tBTM_STATUS BTM_SetInquiryMode (UINT8 mode); + +/******************************************************************************* +** +** Function BTM_SetInquiryScanType +** +** Description This function is called to set the iquiry scan-type to +** standard or interlaced. +** +** Input Params: BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BT_API extern tBTM_STATUS BTM_SetInquiryScanType (UINT16 scan_type); + +/******************************************************************************* +** +** Function BTM_SetPageScanType +** +** Description This function is called to set the page scan-type to +** standard or interlaced. +** +** Input Params: BTM_SCAN_TYPE_STANDARD or BTM_SCAN_TYPE_INTERLACED +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + + BT_API extern tBTM_STATUS BTM_SetPageScanType (UINT16 scan_type); + +/******************************************************************************* +** +** Function BTM_ReadRemoteDeviceName +** +** Description This function initiates a remote device HCI command to the +** controller and calls the callback when the process has completed. +** +** Input Params: remote_bda - device address of name to retrieve +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_BUSY if already in progress +** BTM_UNKNOWN_ADDR if device address is bad +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, + tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_CancelRemoteDeviceName +** +** Description This function initiates the cancel request for the specified +** remote device. +** +** Input Params: None +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if there is not an active remote name request. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CancelRemoteDeviceName (void); + +/******************************************************************************* +** +** Function BTM_ReadRemoteVersion +** +** Description This function is called to read a remote device's version +** +** Returns BTM_SUCCESS if successful, otherwise an error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadRemoteVersion (BD_ADDR addr, + UINT8 *lmp_version, + UINT16 *manufacturer, + UINT16 *lmp_sub_version); + +/******************************************************************************* +** +** Function BTM_ReadRemoteFeatures +** +** Description This function is called to read a remote device's features +** +** Returns pointer to the features string +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr); + +/******************************************************************************* +** +** Function BTM_InqFirstResult +** +** Description This function looks through the inquiry database for the first +** used entrysince the LAST inquiry. This is used in conjunction +** with BTM_InqNext by applications as a way to walk through the +** inquiry results database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ + BTM_API extern tBTM_INQ_INFO *BTM_InqFirstResult (void); + + +/******************************************************************************* +** +** Function BTM_InqNextResult +** +** Description This function looks through the inquiry database for the next +** used entrysince the LAST inquiry. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ + BTM_API extern tBTM_INQ_INFO *BTM_InqNextResult (tBTM_INQ_INFO *p_cur); + + +/******************************************************************************* +** +** Function BTM_InqDbRead +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address. This is the application's +** interface to get the inquiry details of a specific BD address. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ + BTM_API extern tBTM_INQ_INFO *BTM_InqDbRead (BD_ADDR p_bda); + + +/******************************************************************************* +** +** Function BTM_InqDbFirst +** +** Description This function looks through the inquiry database for the first +** used entry, and returns that. This is used in conjunction with +** BTM_InqDbNext by applications as a way to walk through the +** inquiry database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ + BTM_API extern tBTM_INQ_INFO *BTM_InqDbFirst (void); + + +/******************************************************************************* +** +** Function BTM_InqDbNext +** +** Description This function looks through the inquiry database for the next +** used entry, and returns that. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ + BTM_API extern tBTM_INQ_INFO *BTM_InqDbNext (tBTM_INQ_INFO *p_cur); + + +/******************************************************************************* +** +** Function BTM_ClearInqDb +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns BTM_BUSY if an inquiry, get remote name, or event filter +** is active, otherwise BTM_SUCCESS +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ClearInqDb (BD_ADDR p_bda); + + +/******************************************************************************* +** +** Function BTM_ReadNumInqDbEntries +** +** Returns This function returns the number of entries in the inquiry database. +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_ReadNumInqDbEntries (void); + + +/******************************************************************************* +** +** Function BTM_InquiryRegisterForChanges +** +** Description This function is called to register a callback for when the +** inquiry database changes, i.e. new entry or entry deleted. +** +** Returns BTM_SUCCESS if successful, otherwise error code +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_InquiryRegisterForChanges (tBTM_INQ_DB_CHANGE_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ReadInquiryRspTxPower +** +** Description This command will read the inquiry Transmit Power level used +** to transmit the FHS and EIR data packets. +** This can be used directly in the Tx Power Level EIR data type. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadInquiryRspTxPower (tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_WriteInquiryTxPower +** +** Description This command is used to write the inquiry transmit power level +** used to transmit the inquiry (ID) data packets. The Controller +** should use the supported TX power level closest to the Tx_Power +** parameter. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WriteInquiryTxPower (INT8 tx_power); + +/******************************************************************************* +** +** Function BTM_StartDiscovery +** +** Description This function is called by an application (or profile) +** when it wants to trigger an service discovery using the +** BTM's discovery database. +** +** Returns tBTM_STATUS +** BTM_CMD_STARTED if the discovery was initiated +** BTM_BUSY if one is already in progress +** BTM_UNKNOWN_ADDR if no addresses are in the INQ DB +** BTM_ERR_PROCESSING if err initiating the command +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_StartDiscovery (tBTM_CMPL_CB *p_cmpl_cb, + BD_ADDR_PTR p_rem_addr); + + +/******************************************************************************* +** +** Function BTM_FindAttribute +** +** Description This function is called by an application (or profile) +** when it wants to see if an attribute exists in the BTM +** discovery database. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ + BTM_API extern tSDP_DISC_REC *BTM_FindAttribute (UINT16 attr_id, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function BTM_FindService +** +** Description This function is called by an application (or profile) +** when it wants to see if a service exists in the BTM +** discovery database. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ + BTM_API extern tSDP_DISC_REC *BTM_FindService (UINT16 service_uuid, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function BTM_SetDiscoveryParams +** +** Description This function is called to set the BTM default discovery parameters. +** These UUID and attribute filters are used during the call to +** BTM_StartDiscovery. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetDiscoveryParams (UINT16 num_uuid, tSDP_UUID *p_uuid_list, + UINT16 num_attr, UINT16 *p_attr_list); + + +/***************************************************************************** +** ACL CHANNEL MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_SetLinkPolicy +** +** Description Create and send HCI "Write Policy Set" command +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetLinkPolicy (BD_ADDR remote_bda, + UINT16 *settings); + + +/******************************************************************************* +** +** Function BTM_ReadLinkPolicy +** +** Description This function is called to read the link policy settings. +** The address of link policy results are returned in the callback. +** (tBTM_LNK_POLICY_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLinkPolicy (BD_ADDR remote_bda, + tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkPolicy +** +** Description Set the default value for HCI "Write Policy Set" command +** to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetDefaultLinkPolicy (UINT16 settings); + + +/******************************************************************************* +** +** Function BTM_SetDefaultLinkSuperTout +** +** Description Set the default value for HCI "Write Link Supervision Timeout" +** command to use when an ACL link is created. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetDefaultLinkSuperTout (UINT16 timeout); + + +/******************************************************************************* +** +** Function BTM_SetLinkSuperTout +** +** Description Create and send HCI "Write Link Supervision Timeout" command +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetLinkSuperTout (BD_ADDR remote_bda, + UINT16 timeout); + +/******************************************************************************* +** +** Function BTM_RegForLstoEvt +** +** Description register for the HCI "Link Supervision Timeout Change" event +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_RegForLstoEvt (tBTM_LSTO_CBACK *p_cback); + + +/* These next APIs are available if the power manager is not compiled in */ +#if BTM_PWR_MGR_INCLUDED == FALSE +/******************************************************************************* +** +** Function BTM_SetHoldMode +** +** Description This function is called to set a connection into hold mode. +** A check is made if the connection is in sniff or park mode, +** and if yes, the hold mode is ignored. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetHoldMode (BD_ADDR remote_bda, UINT16 min_interval, + UINT16 max_interval); + + +/******************************************************************************* +** +** Function BTM_SetSniffMode +** +** Description This function is called to set a connection into sniff mode. +** A check is made if the connection is already in sniff or park +** mode, and if yes, the sniff mode is ignored. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetSniffMode (BD_ADDR remote_bda, UINT16 min_period, + UINT16 max_period, UINT16 attempt, + UINT16 timeout); + + +/******************************************************************************* +** +** Function BTM_CancelSniffMode +** +** Description This function is called to put a connection out of sniff mode. +** A check is made if the connection is already in sniff mode, +** and if not, the cancel sniff mode is ignored. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CancelSniffMode (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_SetParkMode +** +** Description This function is called to set a connection into park mode. +** A check is made if the connection is already in sniff or park +** mode, and if yes, the park mode is ignored. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetParkMode (BD_ADDR remote_bda, + UINT16 beacon_min_period, + UINT16 beacon_max_period); + + +/******************************************************************************* +** +** Function BTM_CancelParkMode +** +** Description This function is called to put a connection out of park mode. +** A check is made if the connection is already in park mode, +** and if not, the cancel sniff mode is ignored. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CancelParkMode (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_ReadAclMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadAclMode (BD_ADDR remote_bda, UINT8 *p_mode); + +#endif /* if BTM_PWR_MGR_INCLUDED == FALSE */ + + +/******************************************************************************* +** +** Function BTM_SetPacketTypes +** +** Description This function is set the packet types used for a specific +** ACL connection, +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetPacketTypes (BD_ADDR remote_bda, UINT16 pkt_types); + + +/******************************************************************************* +** +** Function BTM_ReadPacketTypes +** +** Description This function is set the packet types used for the specified +** ACL connection, +** +** Returns packet types supported for the connection, or 0 if no BD address +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadPacketTypes (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_IsAclConnectionUp +** +** Description This function is called to check if an ACL connection exists +** to a specific remote BD Address. +** +** Returns TRUE if connection is up, else FALSE. +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_IsAclConnectionUp (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_GetRole +** +** Description This function is called to get the role of the local device +** for the ACL connection with the specified remote device +** +** Returns BTM_SUCCESS if connection exists. +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_GetRole (BD_ADDR remote_bd_addr, UINT8 *p_role); + + + +/******************************************************************************* +** +** Function BTM_SwitchRole +** +** Description This function is called to switch role between master and +** slave. If role is already set it will do nothing. If the +** command was initiated, the callback function is called upon +** completion. +** +** Returns BTM_SUCCESS if already in specified role. +** BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_MODE_UNSUPPORTED if local device does not support role switching +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SwitchRole (BD_ADDR remote_bd_addr, + UINT8 new_role, + tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ChangeLinkKey +** +** Description This function is called to change the link key of the +** connection. +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if the previous command is not completed +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ChangeLinkKey (BD_ADDR remote_bd_addr, + tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ReadRSSI +** +** Description This function is called to read the link policy settings. +** The address of link policy results are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadRSSI (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_ReadTxPower +** +** Description This function is called to read the current connection +** TX power of the connection. The TX power level results +** are returned in the callback. +** (tBTM_RSSI_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_ReadLinkQuality +** +** Description This function is called to read the link quality. +** The value of the link quality is returned in the callback. +** (tBTM_LINK_QUALITY_RESULTS) +** +** Returns BTM_CMD_STARTED if command issued to controller. +** BTM_NO_RESOURCES if couldn't allocate memory to issue command +** BTM_UNKNOWN_ADDR if no active link with bd addr specified +** BTM_BUSY if command is already in progress +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLinkQuality (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_RegBusyLevelNotif +** +** Description This function is called to register a callback to receive +** busy level change events. +** +** Returns BTM_SUCCESS if successfully registered, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level, + tBTM_BL_EVENT_MASK evt_mask); + +/******************************************************************************* +** +** Function BTM_AclRegisterForChanges +** +** Description This function is called to register a callback to receive +** ACL database change events, i.e. new connection or removed. +** +** Returns BTM_SUCCESS if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_AclRegisterForChanges (tBTM_ACL_DB_CHANGE_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_GetNumAclLinks +** +** Description This function is called to count the number of +** ACL links that are active. +** +** Returns UINT16 Number of active ACL links +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_GetNumAclLinks (void); + + +/******************************************************************************* +** +** Function BTM_ReadClockOffset +** +** Description This returns the clock offset for a specific +** ACL connection. +** +** Returns clock-offset or 0 if unknown +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadClockOffset (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_SetQoS +** +** Description This function is called to setup QoS +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetQoS(BD_ADDR bd, FLOW_SPEC *p_flow, + tBTM_CMPL_CB *p_cb); + + +/***************************************************************************** +** (e)SCO CHANNEL MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_CreateSco +** +** Description This function is called to create an SCO connection. If the +** "is_orig" flag is TRUE, the connection will be originated, +** otherwise BTM will wait for the other side to connect. +** +** Returns BTM_UNKNOWN_ADDR if the ACL connection is not up +** BTM_BUSY if another SCO being set up to +** the same BD address +** BTM_NO_RESOURCES if the max SCO limit has been reached +** BTM_CMD_STARTED if the connection establishment is started. +** In this case, "*p_sco_inx" is filled in +** with the sco index used for the connection. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, + UINT16 pkt_types, UINT16 *p_sco_inx, + tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb); + + +/******************************************************************************* +** +** Function BTM_RemoveSco +** +** Description This function is called to remove a specific SCO connection. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_SetScoPacketTypes +** +** Description This function is called to set the packet types used for +** a specific SCO connection, +** +** Parameters pkt_types - One or more of the following +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** +** BTM_SCO_LINK_ALL_MASK - enables all supported types +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types); + + +/******************************************************************************* +** +** Function BTM_ReadScoPacketTypes +** +** Description This function is read the packet types used for a specific +** SCO connection. +** +** Returns One or more of the following (bitmask) +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** +** Returns packet types supported for the connection +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadDeviceScoPacketTypes +** +** Description This function is read the SCO packet types that +** the device supports. +** +** Returns packet types supported by the device. +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadDeviceScoPacketTypes (void); + + +/******************************************************************************* +** +** Function BTM_ReadScoHandle +** +** Description This function is used to read the HCI handle used for a specific +** SCO connection, +** +** Returns handle for the connection, or 0xFFFF if invalid SCO index. +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadScoHandle (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadScoBdAddr +** +** Description This function is read the remote BD Address for a specific +** SCO connection, +** +** Returns pointer to BD address or NULL if not known +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_ReadScoBdAddr (UINT16 sco_inx); + + +/******************************************************************************* +** +** Function BTM_ReadScoDiscReason +** +** Description This function is returns the reason why an (e)SCO connection +** has been removed. It contains the value until read, or until +** another (e)SCO connection has disconnected. +** +** Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_ReadScoDiscReason (void); + + +/******************************************************************************* +** +** Function BTM_SetEScoMode +** +** Description This function sets up the negotiated parameters for SCO or +** eSCO, and sets as the default mode used for calls to +** BTM_CreateSco. It can be called only when there are no +** active (e)SCO links. +** +** Returns BTM_SUCCESS if the successful. +** BTM_BUSY if there are one or more active (e)SCO links. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, + tBTM_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_SetWBSCodec +** +** Description This function sends command to the controller to setup +** WBS codec for the upcoming eSCO connection. +** +** Returns BTM_SUCCESS. +** +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_SetWBSCodec (tBTM_SCO_CODEC_TYPE codec_type); + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, + tBTM_ESCO_CBACK *p_esco_cback); + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_ILLEGAL_VALUE if no connection for specified sco_inx. +** BTM_MODE_UNSUPPORTED if local controller does not support +** 1.2 specification. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, + tBTM_ESCO_DATA *p_parms); + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_ILLEGAL_VALUE if no connection for specified sco_inx. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_MODE_UNSUPPORTED if local controller does not support +** 1.2 specification. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, + tBTM_CHG_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_EScoConnRsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then values set through BTM_SetEScoMode +** are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, + tBTM_ESCO_PARAMS *p_parms); + +/******************************************************************************* +** +** Function BTM_GetNumScoLinks +** +** Description This function returns the number of active SCO links. +** +** Returns UINT8 +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_GetNumScoLinks (void); + +/***************************************************************************** +** SECURITY MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_SecRegister +** +** Description Application manager calls this function to register for +** security services. There can be one and only one application +** saving link keys. BTM allows only first registration. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecRegister (tBTM_APPL_INFO *p_cb_info); + + +/******************************************************************************* +** +** Function BTM_SecRegisterLinkKeyNotificationCallback +** +** Description Profiles can register to be notified when a new Link Key +** is generated per connection. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecRegisterLinkKeyNotificationCallback (tBTM_LINK_KEY_CALLBACK *p_callback); + + +/******************************************************************************* +** +** Function BTM_SecAddRmtNameNotifyCallback +** +** Description Profiles can register to be notified when name of the +** remote device is resolved (up to BTM_SEC_MAX_RMT_NAME_CALLBACKS). +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecAddRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback); + + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description A profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecDeleteRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback); + + +/******************************************************************************* +** +** Function BTM_SecSetConnectFilterCallback +** +** Description Host can register to be asked whenever an HCI connection +** request is received. In the registered function host +** suppose to check connectibility filters. Yes/No result +** should be returned synchronously. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SecSetConnectFilterCallback (tBTM_FILTER_CB *p_callback); + + +/******************************************************************************* +** +** Function BTM_GetSecurityMode +** +** Description Get security mode for the device +** +** Returns void +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_GetSecurityMode (void); + + +/******************************************************************************* +** +** Function BTM_GetSecurityFlags +** +** Description Get security flags for the device +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 * p_sec_flags); + +/******************************************************************************* +** +** Function BTM_ReadTrustedMask +** +** Description Get trusted mask for the device +** +** Returns NULL, if the device record is not found. +** otherwise, the trusted mask +** +*******************************************************************************/ + BTM_API extern UINT32 * BTM_ReadTrustedMask (BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function BTM_SetSecurityMode +** +** Description Set security mode for the device +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetSecurityMode (UINT8 sec_mode); + + +/******************************************************************************* +** +** Function BTM_SetPinType +** +** Description Set PIN type for the device. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len); + + +/******************************************************************************* +** +** Function BTM_SetPairableMode +** +** Description Enable or disable pairing +** +** Parameters allow_pairing - (TRUE or FALSE) whether or not the device +** allows pairing. +** connect_only_paired - (TRUE or FALSE) whether or not to +** only allow paired devices to connect. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_paired); + +/******************************************************************************* +** +** Function BTM_SetSecurityLevel +** +** Description Register service security level with Security Manager. Each +** service must register its requirements regardless of the +** security level that is used. This API is called once for originators +** nad again for acceptors of connections. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, + UINT8 service_id, UINT16 sec_level, + UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); + +/******************************************************************************* +** +** Function BTM_SetUCDSecurityLevel +** +** Description Register UCD service security level with Security Manager. Each +** service must register its requirements regardless of the +** security level that is used. This API is called once for originators +** and again for acceptors of connections. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SetUCDSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); + +/******************************************************************************* +** +** Function BTM_SetOutService +** +** Description This function is called to set the service for +** outgoing connection. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id); + +/******************************************************************************* +** +** Function BTM_SecClrService +** +** Description Removes specified service record(s) from the security database. +** All service records with the specified name are removed. +** Typically used only by devices with limited RAM so that it can +** reuse an old security service record. +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_SecClrService (UINT8 service_id); + +/******************************************************************************* +** +** Function BTM_SecClrUCDService +** +** Description +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records. +** +** Returns Number of records that were freed. +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_SecClrUCDService (UINT8 service_id); + +/******************************************************************************* +** +** Function BTM_SecAddDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** stored in the NVRAM. +** dev_class, bd_name, link_key, and features are NULL if unknown +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, + BD_NAME bd_name, BD_FEATURES features, + UINT32 trusted_mask[], LINK_KEY link_key, + UINT8 key_type, tBTM_IO_CAP io_cap); + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Returns TRUE if rmoved OK, FALSE if not found +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function BTM_SecUseMasterLinkKey +** +** Description This function is called to tell master of the piconet to +** switch to master link key +** +** Returns BTM_SUCCESS if command is successully initiated +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SecUseMasterLinkKey (BOOLEAN use_master_key); + + +/******************************************************************************* +** +** Function BTM_SetMasterKeyCompCback +** +** Description This function is called to register for the master key complete +** status event. +** +** Parameters: mkey_cback - callback registered with the security manager +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SetMasterKeyCompCback(tBTM_MKEY_CALLBACK *mkey_cback ); + + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKey +** +** Description This function is called to obtain link key for the device +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Returns BTM_SUCCESS if successful, otherwise error code +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SecGetDeviceLinkKey (BD_ADDR bd_addr, + LINK_KEY link_key); + + +/******************************************************************************* +** +** Function BTM_PINCodeReply +** +** Description This function is called after Security Manager submitted +** PIN code request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, + UINT8 *p_pin, UINT32 trusted_mask[]); + + +/******************************************************************************* +** +** Function BTM_DeviceAuthorized +** +** Description This function is called after Security Manager submitted +** authorization request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_DeviceAuthorized (BD_ADDR bd_addr, UINT8 res, + UINT32 trusted_mask[]); + + +/******************************************************************************* +** +** Function BTM_SecBond +** +** Description This function is called to perform bonding with peer device. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]); + +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Returns BTM_CMD_STARTED if successfully initiated, otherwise error +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_SetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed. Can be set to NULL +** if status is not desired. +** p_ref_data - pointer to any data the caller wishes to receive +** in the callback function upon completion. +* can be set to NULL if not used. +** +** Returns BTM_SUCCESS - already encrypted +** BTM_PENDING - command will be returned in the callback +** BTM_WRONG_MODE- connection not up. +** BTM_BUSY - security procedures are currently active +** BTM_MODE_UNSUPPORTED - if security manager not linked in. +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBTM_SEC_CBACK *p_callback, + void *p_ref_data); + +/******************************************************************************* +** +** Function BTM_ConfirmReqReply +** +** Description This function is called to confirm the numeric value for +** Simple Pairing in response to BTM_SP_CFM_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** +*******************************************************************************/ + BTM_API extern void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function BTM_PasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTM_SP_KEY_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** passkey - numeric value in the range of 0 - 999999(0xF423F). +** +*******************************************************************************/ + BTM_API extern void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey); + +/******************************************************************************* +** +** Function BTM_SendKeypressNotif +** +** Description This function is used during the passkey entry model +** by a device with KeyboardOnly IO capabilities +** (very likely to be a HID Device). +** It is called by a HID Device to inform the remote device when +** a key has been entered or erased. +** +** Parameters: bd_addr - Address of the peer device +** type - notification type +** +*******************************************************************************/ + BTM_API extern void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type); + +/******************************************************************************* +** +** Function BTM_IoCapRsp +** +** Description This function is called in response to BTM_SP_IO_REQ_EVT +** When the event data io_req.oob_data is set to BTM_OOB_UNKNOWN +** by the tBTM_SP_CALLBACK implementation, this function is +** called to provide the actual response +** +** Parameters: bd_addr - Address of the peer device +** io_cap - The IO capability of local device. +** oob - BTM_OOB_NONE or BTM_OOB_PRESENT. +** auth_req- MITM protection required or not. +** +*******************************************************************************/ + BTM_API extern void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, + tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req); + +/******************************************************************************* +** +** Function BTM_ReadLocalOobData +** +** Description This function is called to read the local OOB data from +** LM +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadLocalOobData(void); + +/******************************************************************************* +** +** Function BTM_RemoteOobDataReply +** +** Description This function is called to provide the remote OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** +*******************************************************************************/ + BTM_API extern void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, BT_OCTET16 c, BT_OCTET16 r); + +/******************************************************************************* +** +** Function BTM_BuildOobData +** +** Description This function is called to build the OOB data payload to +** be sent over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** max_len - p_data size. +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** name_len- 0, local device name would not be included. +** otherwise, the local device name is included for +** up to this specified length +** +** Returns Number of bytes in p_data. +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, + BT_OCTET16 r, UINT8 name_len); + +/******************************************************************************* +** +** Function BTM_ReadOobData +** +** Description This function is called to parse the OOB data payload +** received over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** eir_tag - The associated EIR tag to read the data. +** *p_len(output) - the length of the data with the given tag. +** +** Returns the beginning of the data with the given tag. +** NULL, if the tag is not found. +** +*******************************************************************************/ + BTM_API extern UINT8 * BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len); + +/******************************************************************************* +** +** Function BTM_SecReadDevName +** +** Description Looks for the device name in the security database for the +** specified BD address. +** +** Returns Pointer to the name or NULL +** +*******************************************************************************/ + BTM_API extern char *BTM_SecReadDevName (BD_ADDR bd_addr); + + +/***************************************************************************** +** POWER MANAGEMENT FUNCTIONS +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_PmRegister +** +** Description register or deregister with power manager +** +** Returns BTM_SUCCESS if successful, +** BTM_NO_RESOURCES if no room to hold registration +** BTM_ILLEGAL_VALUE +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, + tBTM_PM_STATUS_CBACK *p_cb); + + +/******************************************************************************* +** +** Function BTM_SetPowerMode +** +** Description store the mode in control block or +** alter ACL connection behavior. +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, + tBTM_PM_PWR_MD *p_mode); + + +/******************************************************************************* +** +** Function BTM_ReadPowerMode +** +** Description This returns the current mode for a specific +** ACL connection. +** +** Input Param remote_bda - device address of desired ACL connection +** +** Output Param p_mode - address where the current mode is copied into. +** BTM_ACL_MODE_NORMAL +** BTM_ACL_MODE_HOLD +** BTM_ACL_MODE_SNIFF +** BTM_ACL_MODE_PARK +** (valid only if return code is BTM_SUCCESS) +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, + tBTM_PM_MODE *p_mode); + +/******************************************************************************* +** +** Function BTM_SetSsrParams +** +** Description This sends the given SSR parameters for the given ACL +** connection if it is in ACTIVE mode. +** +** Input Param remote_bda - device address of desired ACL connection +** max_lat - maximum latency (in 0.625ms)(0-0xFFFE) +** min_rmt_to - minimum remote timeout +** min_loc_to - minimum local timeout +** +** +** Returns BTM_SUCCESS if the HCI command is issued successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** BTM_CMD_STORED if the command is stored +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SetSsrParams (BD_ADDR remote_bda, UINT16 max_lat, + UINT16 min_rmt_to, UINT16 min_loc_to); + +/******************************************************************************* +** +** Function BTM_IsPowerManagerOn +** +** Description This function is called to check if power manager is included. +** in the BTE version. +** +** Returns TRUE if power manager is compiled in, otherwise FALSE. +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_IsPowerManagerOn (void); + + +/******************************************************************************* +** +** Function BTM_GetHCIConnHandle +** +** Description This function is called to get the handle for an ACL connection +** to a specific remote BD Address. +** +** Returns the handle of the connection, or 0xFFFF if none. +** +*******************************************************************************/ + BTM_API extern UINT16 BTM_GetHCIConnHandle (BD_ADDR remote_bda); + + +/******************************************************************************* +** +** Function BTM_ReadStoredLinkKey +** +** Description This function is called to obtain link key for the specified +** device from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Address of the device +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ReadStoredLinkKey (BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_WriteStoredLinkKey +** +** Description This function is called to write link keys for the specified +** device addresses to the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: num_keys - Number of link keys +** bd_addr - Addresses of the devices +** link_key - Link Keys to be stored +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WriteStoredLinkKey (UINT8 num_keys, + BD_ADDR *bd_addr, + LINK_KEY *link_key, + tBTM_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function BTM_DeleteStoredLinkKey +** +** Description This function is called to delete link key for the specified +** device addresses from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Addresses of the devices +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function BTM_WriteEIR +** +** Description This function is called to write EIR data to controller. +** +** Parameters p_buff - allocated HCI command buffer including extended +** inquriry response +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WriteEIR( BT_HDR * p_buff ); + +/******************************************************************************* +** +** Function BTM_CheckEirData +** +** Description This function is called to get EIR data from significant part. +** +** Parameters p_eir - pointer of EIR significant part +** type - finding EIR data type +** p_length - return the length of EIR data +** +** Returns pointer of EIR data +** +*******************************************************************************/ + BTM_API extern UINT8 *BTM_CheckEirData( UINT8 *p_eir, UINT8 type, UINT8 *p_length ); + +/******************************************************************************* +** +** Function BTM_HasEirService +** +** Description This function is called to know if UUID in bit map of UUID. +** +** Parameters p_eir_uuid - bit map of UUID list +** uuid16 - UUID 16-bit +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ + BTM_API extern BOOLEAN BTM_HasEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_HasInquiryEirService +** +** Description This function is called to know if UUID in bit map of UUID list. +** +** Parameters p_results - inquiry results +** uuid16 - UUID 16-bit +** +** Returns BTM_EIR_FOUND - if found +** BTM_EIR_NOT_FOUND - if not found and it is complete list +** BTM_EIR_UNKNOWN - if not found and it is not complete list +** +*******************************************************************************/ + BTM_API extern tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService( tBTM_INQ_RESULTS *p_results, + UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_AddEirService +** +** Description This function is called to add a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ + BTM_API extern void BTM_AddEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_RemoveEirService +** +** Description This function is called to remove a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ + BTM_API extern void BTM_RemoveEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ); + +/******************************************************************************* +** +** Function BTM_GetEirSupportedServices +** +** Description This function is called to get UUID list from bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** p - reference of current pointer of EIR +** max_num_uuid16 - max number of UUID can be written in EIR +** num_uuid16 - number of UUID have been written in EIR +** +** Returns BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_GetEirSupportedServices( UINT32 *p_eir_uuid, UINT8 **p, + UINT8 max_num_uuid16, UINT8 *p_num_uuid16); + +/******************************************************************************* +** +** Function BTM_GetEirUuidList +** +** Description This function parses EIR and returns UUID list. +** +** Parameters p_eir - EIR +** uuid_size - LEN_UUID_16, LEN_UUID_32, LEN_UUID_128 +** p_num_uuid - return number of UUID in found list +** p_uuid_list - return UUID 16-bit list +** max_num_uuid - maximum number of UUID to be returned +** +** Returns 0 - if not found +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE +** BTM_EIR_MORE_16BITS_UUID_TYPE +** BTM_EIR_COMPLETE_32BITS_UUID_TYPE +** BTM_EIR_MORE_32BITS_UUID_TYPE +** BTM_EIR_COMPLETE_128BITS_UUID_TYPE +** BTM_EIR_MORE_128BITS_UUID_TYPE +** +*******************************************************************************/ + BTM_API extern UINT8 BTM_GetEirUuidList( UINT8 *p_eir, UINT8 uuid_size, UINT8 *p_num_uuid, + UINT8 *p_uuid_list, UINT8 max_num_uuid); + +/******************************************************************************* +** +** Function BTM_SyncStack +** +** Description For Dual-Stack support. Called to initiate switching to/from +** main stack (running on phone baseband) to mm stack (light +** stack running on multi-media chip) +** +** Parameters sync_dir: BTM_SW_BB_TO_MM: switch from BB to MM stack +** BTM_SW_MM_TO_BB: switch from MM to BB stack +** BTM_SW_RESYNC: resync MM and BB stacks +** +** p_sync_cback: callback function for event notification +** Returns +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_SyncStack(tBTM_SW_DIR sync_dir, tBTM_SYNC_STACK_CBACK p_sync_cback); + +/******************************************************************************* +** +** Function BTM_SyncBtaRsp +** +** Description For Dual-Stack support. Called to indicate that upper layers +** (e.g. BTA or application) have completed synchronizing bta/app +** specific layers for switching. +** +** Called in response to 'BTM_SYNC_BTA_EVT' +** +** Parameters status: BTM_SUCESS: bta/app successfully synchronized +** otherwise: sync was unsuccessfule. Abort switch. +** +** p_btm_sync_info: information from bta/app that will be needed +** by BTE (avdt and l2cap) for switching. +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_SyncBtaRsp(tBTM_STATUS status, tBTM_SYNC_INFO *p_btm_sync_info); + +/******************************************************************************* +** +** Function BTM_OpenUIPC +** +** Description For Dual-Stack support. Called to open UIPC between +** main stack (running on phone baseband) to embedded light stack +** (running on Multimedia or Bluetooth Controller chip) +** +** Parameters sync_dir: BTM_SW_BB_TO_MM: switch from BB to MM stack +** BTM_SW_BB_TO_BTC:switch from BB to BTC stack +** +** p_sync_callback: callback function for event notification +** Returns +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_OpenUIPC(tBTM_SW_DIR sync_dir, tBTM_SYNC_STACK_CBACK p_sync_callback); + +/******************************************************************************* +** +** Function BTM_CloseUIPC +** +** Description For Dual-Stack support. Called to close UIPC between +** main stack (running on phone baseband) to embedded light stack +** (running on Multimedia or Bluetooth Controller chip) +** +** Parameters +** p_sync_callback: callback function for event notification +** Returns +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_CloseUIPC(tBTM_SYNC_STACK_CBACK p_sync_callback); + +/******************************************************************************* +** +** Function BTM_IpcSend +** +** Description For Dual-Stack support. Called to send ipc messages from +** full stack to lite stack and vice-versa. This API is +** typically called by bta layers e.g. bta_av. +** +** +** Parameters len: Length of the buffer in the ipc message +** +** buffer: Pointer to the buffer to be passed in the IPC message +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_IpcSend(UINT16 len, UINT8* buffer); + +/******************************************************************************* +** +** Function BTM_IpcSendBuf +** +** Description For Dual-Stack support. Called to send ipc messages from +** full stack to lite stack and vice-versa. This API is +** typically called by bta layers e.g. bta_av_sync. +** +** +** Parameters p_buf: Pointer to the buffer to be passed in the IPC message +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_IpcSendBuf(BT_HDR* p_buf); + +/******************************************************************************* +** +** Function BTM_RegIpcEvtHandler +** +** Description registers the DM provided handler for IPC events +** +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_RegIpcEvtHandler(tBTM_IPC_EVT_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTM_RegRTIpcEvtHandler +** +** Description registers the RT(Audio Routing) provided handler for IPC events +** +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_RegRTIpcEvtHandler(tBTM_IPC_EVT_CBACK *p_cback); + +/***************************************************************************** +** N2BT +*****************************************************************************/ + +/* Data callback for N2BT */ + typedef void (tBTM_N2BT_DATA_CB) (BD_ADDR bd_addr, UINT16 handle, UINT8 *p_data, UINT16 datalen); + +/******************************************************************************* +** +** Function BTM_N2BtAcquire +** +** Description Put controller into acquisition mode +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_N2BtAcquire(BD_ADDR bd_addr, UINT16 timeout, + UINT8 freq, UINT8 src_addrlen, UINT8 sensor_flags, + UINT8 sensor_type, UINT8 sensor_clk_accuracy, + UINT16 add_rx_window, UINT16 init_crc, + UINT32 ac_low, UINT32 ac_high, UINT16 pkt_hdr, + UINT16 list_dur, UINT16 list_int, + UINT8 oor_missed_pkts, tBTM_VSC_CMPL_CB *p_cb, + tBTM_N2BT_DATA_CB *p_data_cback); + +/******************************************************************************* +** +** Function BTM_N2BtDisconnect +** +** Description Disconnects all N2BT devices +** +** Returns void +** +*******************************************************************************/ + BTM_API extern void BTM_N2BtDisconnect(void); + + +/******************************************************************************* +** +** Function BTM_ConfigI2SPCM +** +** Description This function sends VSC Write_I2SPCM_Interface_Param +** as to the specified codec_type. +** +** +** Parameter codec_type: codec_type to be used for sco connection. +** role: master or slave role +** sample_rate: sampling rate +** clock_rate:clock rate 128K to 2048K +** +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE: wrong codec type +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ConfigI2SPCM (tBTM_SCO_CODEC_TYPE codec_type, UINT8 role, UINT8 sample_rate, UINT8 clock_rate); + +/***************************************************************************** +** SCO OVER HCI +*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_ConfigScoPath +** +** Description This function enable/disable SCO over HCI and registers SCO +** data callback if SCO over HCI is enabled. +** +** Parameter path: SCO or HCI +** p_sco_data_cb: callback function or SCO data if path is set +** to transport. +** p_pcm_param: pointer to the PCM interface parameter. If a NULL +** pointer is used, PCM parameter maintained in +** the control block will be used; otherwise update +** control block value. +** err_data_rpt: Lisbon feature to enable the erronous data report +** or not. +** +** Returns BTM_SUCCESS if the successful. +** BTM_NO_RESOURCES: no rsource to start the command. +** BTM_ILLEGAL_VALUE: invalid callback function pointer. +** BTM_CMD_STARTED :Command sent. Waiting for command cmpl event. +** +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_ConfigScoPath (tBTM_SCO_ROUTE_TYPE path, + tBTM_SCO_DATA_CB *p_sco_data_cb, + tBTM_SCO_PCM_PARAM *p_pcm_param, + BOOLEAN err_data_rpt); + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** +** +*******************************************************************************/ + BTM_API extern tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function BTM_SetARCMode +** +** Description Send Audio Routing Control command. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_SetARCMode (UINT8 iface, UINT8 arc_mode, tBTM_VSC_CMPL_CB *p_arc_cb); + + +/******************************************************************************* +** +** Function BTM_PCM2Setup_Write +** +** Description Send PCM2_Setup write command. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_PCM2Setup_Write (BOOLEAN clk_master, tBTM_VSC_CMPL_CB *p_arc_cb); + +#ifdef __cplusplus +} +#endif + +#endif /* BTM_API_H */ diff --git a/stack/include/btm_ble_api.h b/stack/include/btm_ble_api.h new file mode 100644 index 0000000..8456b9f --- /dev/null +++ b/stack/include/btm_ble_api.h @@ -0,0 +1,729 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the Bluetooth Manager (BTM) API function external + * definitions. + * + ******************************************************************************/ +#ifndef BTM_BLE_API_H +#define BTM_BLE_API_H + +#include "btm_api.h" + +#define CHNL_MAP_LEN 5 +typedef UINT8 tBTM_BLE_CHNL_MAP[CHNL_MAP_LEN]; + +#define BTM_BLE_SCAN_MODE_PASS 0 +#define BTM_BLE_SCAN_MODE_ACTI 1 +#define BTM_BLE_SCAN_MODE_NONE 0xff +typedef UINT8 tBTM_BLE_SCAN_MODE; + +/* advertising channel map */ +#define BTM_BLE_ADV_CHNL_37 (0x01 << 0) +#define BTM_BLE_ADV_CHNL_38 (0x01 << 1) +#define BTM_BLE_ADV_CHNL_39 (0x01 << 2) +typedef UINT8 tBTM_BLE_ADV_CHNL_MAP; + +/*d efault advertising channel map */ +#ifndef BTM_BLE_DEFAULT_ADV_CHNL_MAP +#define BTM_BLE_DEFAULT_ADV_CHNL_MAP (BTM_BLE_ADV_CHNL_37| BTM_BLE_ADV_CHNL_38| BTM_BLE_ADV_CHNL_39) +#endif + +/* advertising filter policy */ +#define AP_SCAN_CONN_ALL 0x00 /* default */ +#define AP_SCAN_WL_CONN_ALL 0x01 +#define AP_SCAN_ALL_CONN_WL 0x02 +#define AP_SCAN_CONN_WL 0x03 +#define AP_SCAN_CONN_POLICY_MAX 0x04 +typedef UINT8 tBTM_BLE_AFP; + +/* default advertising filter policy */ +#ifndef BTM_BLE_DEFAULT_AFP +#define BTM_BLE_DEFAULT_AFP AP_SCAN_CONN_ALL +#endif + +/* scanning filter policy */ +#define SP_ADV_ALL 0x00 /* accept adv pakt from all, directed adv pkt not directed to me is ignored */ +#define SP_ADV_WL 0x01 /* accept adv pakt from device in white list, directed adv pkt not directed to me is ignored */ +typedef UINT8 tBTM_BLE_SFP; + +#ifndef BTM_BLE_DEFAULT_SFP +#define BTM_BLE_DEFAULT_SFP SP_ADV_ALL +#endif + +/* adv parameter boundary values */ +#define BTM_BLE_ADV_INT_MIN 0x0020 +#define BTM_BLE_ADV_INT_MAX 0x4000 + +/* connection parameter boundary value */ +#define BTM_BLE_SCAN_INT_MIN 0x0004 +#define BTM_BLE_SCAN_INT_MAX 0x4000 +#define BTM_BLE_SCAN_WIN_MIN 0x0004 +#define BTM_BLE_SCAN_WIN_MAX 0x4000 +#define BTM_BLE_CONN_INT_MIN 0x0006 +#define BTM_BLE_CONN_INT_MAX 0x0C80 +#define BTM_BLE_CONN_LATENCY_MAX 500 +#define BTM_BLE_CONN_SUP_TOUT_MIN 0x000A +#define BTM_BLE_CONN_SUP_TOUT_MAX 0x0C80 +#define BTM_BLE_CONN_PARAM_UNDEF 0xffff /* use this value when a specific value not to be overwritten */ + +/* default connection parameters if not configured, use GAP recommend value for auto/selective connection */ +/* default scan interval */ +#ifndef BTM_BLE_CONN_EST_SCAN_INT +#define BTM_BLE_CONN_EST_SCAN_INT 96 /* 312.5 ms = 500 *0.625 */ +#endif +/* default scan window for background connection, applicable for auto connection or selective conenction */ +#ifndef BTM_BLE_CONN_EST_SCAN_WIND +#define BTM_BLE_CONN_EST_SCAN_WIND 48 /* 187.5 ms = 400 *0.625 */ +#endif + +/* default scan paramter used in reduced power cycle */ +#ifndef BTM_BLE_CONN_EST_SCAN_INT_LO +#define BTM_BLE_CONN_EST_SCAN_INT_LO 2048 /* 1.28 s = 500 *0.625 */ +#endif +#ifndef BTM_BLE_CONN_EST_SCAN_WIND_LO +#define BTM_BLE_CONN_EST_SCAN_WIND_LO 18 /* 11.25 ms = 400 *0.625 */ +#endif + +/* default connection interval min */ +#ifndef BTM_BLE_CONN_INT_MIN_DEF +#define BTM_BLE_CONN_INT_MIN_DEF 40 /* 50ms = 400 * 1.25 */ +#endif +/* default connectino interval max */ +#ifndef BTM_BLE_CONN_INT_MAX_DEF +#define BTM_BLE_CONN_INT_MAX_DEF 56 /* 70ms = 56 * 1.25 */ +#endif +/* default slave latency */ +#ifndef BTM_BLE_CONN_SLAVE_LATENCY_DEF +#define BTM_BLE_CONN_SLAVE_LATENCY_DEF 0 /* 0 */ +#endif +/* default supervision timeout */ +#ifndef BTM_BLE_CONN_TIMEOUT_DEF +#define BTM_BLE_CONN_TIMEOUT_DEF 2000 +#endif + +#define BTM_CMAC_TLEN_SIZE 8 /* 64 bits */ +#define BTM_BLE_AUTH_SIGN_LEN 12 /* BLE data signature length 8 Bytes + 4 bytes counter*/ +typedef UINT8 BLE_SIGNATURE[BTM_BLE_AUTH_SIGN_LEN]; /* Device address */ + +/* Structure returned with Rand/Encrypt complete callback */ +typedef struct +{ + UINT8 status; + UINT8 param_len; + UINT16 opcode; + UINT8 param_buf[BT_OCTET16_LEN]; +} tBTM_RAND_ENC; + +/* General callback function for notifying an application that a synchronous +** BTM function is complete. The pointer contains the address of any returned data. +*/ +typedef void (tBTM_RAND_ENC_CB) (tBTM_RAND_ENC *p1); + +#define BTM_BLE_FILTER_TARGET_SCANNER 0x01 +#define BTM_BLE_FILTER_TARGET_ADVR 0x00 + +#define BTM_BLE_POLICY_BLACK_ALL 0x00 /* relevant to both */ +#define BTM_BLE_POLICY_ALLOW_SCAN 0x01 /* relevant to advertiser */ +#define BTM_BLE_POLICY_ALLOW_CONN 0x02 /* relevant to advertiser */ +#define BTM_BLE_POLICY_WHITE_ALL 0x03 /* relevant to both */ + +typedef struct +{ + UINT8 adv_int_min; + UINT8 adv_int_max; + tBTM_BLE_CHNL_MAP chnl_map; + +}tBTM_BLE_ADV_PARAMS; + +/* ADV data flag bit definition used for BTM_BLE_AD_TYPE_FLAG */ +#define BTM_BLE_LIMIT_DISC_FLAG (0x01 << 0) +#define BTM_BLE_GEN_DISC_FLAG (0x01 << 1) +#define BTM_BLE_BREDR_NOT_SPT (0x01 << 2) +#define BTM_BLE_NON_LIMIT_DISC_FLAG (0x00 ) /* lowest bit unset */ +#define BTM_BLE_ADV_FLAG_MASK (BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_BREDR_NOT_SPT | BTM_BLE_GEN_DISC_FLAG) +#define BTM_BLE_LIMIT_DISC_MASK (BTM_BLE_LIMIT_DISC_FLAG ) + +#define BTM_BLE_AD_BIT_DEV_NAME (0x0001 << 0) +#define BTM_BLE_AD_BIT_FLAGS (0x0001 << 1) +#define BTM_BLE_AD_BIT_MANU (0x0001 << 2) +#define BTM_BLE_AD_BIT_TX_PWR (0x0001 << 3) +#define BTM_BLE_AD_BIT_ATTR (0x0001 << 4) +#define BTM_BLE_AD_BIT_INT_RANGE (0x0001 << 5) +#define BTM_BLE_AD_BIT_SERVICE (0x0001 << 6) +#define BTM_BLE_AD_BIT_SERVICE_SOL (0x0001 << 7) +#define BTM_BLE_AD_BIT_SERVICE_DATA (0x0001 << 8) +#define BTM_BLE_AD_BIT_SIGN_DATA (0x0001 << 9) +#define BTM_BLE_AD_BIT_PROPRIETARY (0x0001 << 15) + +typedef UINT16 tBTM_BLE_AD_MASK; + +#define BTM_BLE_AD_TYPE_FLAG 0x01 +#define BTM_BLE_AD_TYPE_SRV_PART 0x02 +#define BTM_BLE_AD_TYPE_SRV_CMPL 0x03 +#define BTM_BLE_AD_TYPE_NAME_SHORT 0x08 +#define BTM_BLE_AD_TYPE_NAME_CMPL 0x09 +#define BTM_BLE_AD_TYPE_TX_PWR 0x0A +#define BTM_BLE_AD_TYPE_DEV_CLASS 0x0D +#define BTM_BLE_AD_TYPE_ATTR 0x10 +#define BTM_BLE_AD_TYPE_MANU 0xff +#define BTM_BLE_AD_TYPE_INT_RANGE 0x12 +#define BTM_BLE_AD_TYPE_SOL_SRV_UUID 0x14 +typedef UINT8 tBTM_BLE_AD_TYPE; + +/* slave preferred connection interval range */ +typedef struct +{ + UINT16 low; + UINT16 hi; + +}tBTM_BLE_INT_RANGE; + +/* Service tag supported in the device */ +typedef struct +{ + UINT8 num_service; + BOOLEAN list_cmpl; + UINT16 *p_uuid; +}tBTM_BLE_SERVICE; + +/* attribute data */ +typedef struct +{ + UINT16 uuid; + UINT16 data_len; + UINT8 *p_data; +}tBTM_BLE_ATTR; + +#ifndef BTM_BLE_NUM_AD_ATTR_MAX +#define BTM_BLE_NUM_AD_ATTR_MAX 10 +#endif +/* attribute list contained in adv data */ +typedef struct +{ + UINT8 num_attr; + tBTM_BLE_ATTR attr_list[BTM_BLE_NUM_AD_ATTR_MAX]; +}tBTM_BLE_ATTR_DATA; + +typedef struct +{ + UINT8 len; + UINT8 *p_val; +}tBTM_BLE_MANU; + +typedef struct +{ + UINT8 adv_type; + UINT8 len; + UINT8 *p_val; /* number of len byte */ +}tBTM_BLE_PROP_ELEM; + +typedef struct +{ + UINT8 num_elem; + tBTM_BLE_PROP_ELEM *p_elem; +}tBTM_BLE_PROPRIETARY; + +typedef struct +{ + tBTM_BLE_MANU manu; /* manufactuer data */ + tBTM_BLE_INT_RANGE int_range; /* slave prefered conn interval range */ + tBTM_BLE_SERVICE services; /* services */ + tBTM_BLE_ATTR_DATA attr; /* attribute data */ + UINT8 flag; + tBTM_BLE_PROPRIETARY *p_proprietary; +}tBTM_BLE_ADV_DATA; + +/* These are the fields returned in each device adv packet. It +** is returned in the results callback if registered. +*/ +typedef struct +{ + UINT8 conn_mode; + tBTM_BLE_AD_MASK ad_mask; /* mask of the valid adv data field */ + UINT8 flag; + UINT8 tx_power_level; + UINT8 remote_name_len; + UINT8 *p_remote_name; + tBTM_BLE_ATTR_DATA attr_data; + tBTM_BLE_SERVICE service; +} tBTM_BLE_INQ_DATA; + +enum +{ + BTM_BLE_CONN_NONE, + BTM_BLE_CONN_AUTO, + BTM_BLE_CONN_SELECTIVE +}; +typedef UINT8 tBTM_BLE_CONN_TYPE; + +typedef BOOLEAN (tBTM_BLE_SEL_CBACK)(BD_ADDR random_bda, UINT8 *p_remote_name); + +/* callback function for SMP signing algorithm, signed data in little endian order with tlen bits long */ +typedef void (tBTM_BLE_SIGN_CBACK)(void *p_ref_data, UINT8 *p_signing_data); +typedef void (tBTM_BLE_VERIFY_CBACK)(void *p_ref_data, BOOLEAN match); +/* random address set complete callback */ +typedef void (tBTM_BLE_RANDOM_SET_CBACK) (BD_ADDR random_bda); + +typedef void (tBTM_BLE_SCAN_REQ_CBACK)(BD_ADDR remote_bda, tBLE_ADDR_TYPE addr_type, UINT8 adv_evt); +/***************************************************************************** +** EXTERNAL FUNCTION DECLARATIONS +*****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif +/******************************************************************************* +** +** Function BTM_SecAddBleDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** bd_name - Name of the peer device. NULL if unknown. +** dev_type - Remote device's device type. +** addr_type - LE device address type. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, + tBT_DEVICE_TYPE dev_type, tBLE_ADDR_TYPE addr_type); + +/******************************************************************************* +** +** Function BTM_SecAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +* +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, + tBTM_LE_KEY_TYPE key_type); + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_BleSetAdvParams(UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP chnl_map); + +/******************************************************************************* +** +** Function BTM_BleWriteAdvData +** +** Description This function is called to write advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_BleReadAdvParams (UINT16 *adv_int_min, UINT16 *adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP *p_chnl_map); + + +/******************************************************************************* +** +** Function BTM_BleSetScanParams +** +** Description This function is called to set Scan parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** scan_type: scan mode. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_BleSetScanParams(UINT16 scan_interval, UINT16 scan_window, + tBTM_BLE_SCAN_MODE scan_type); + +/******************************************************************************* +** +** Function BTM_BleWriteScanRsp +** +** Description This function is called to write LE scan response. +** +** Parameters: p_scan_rsp: scan response. +** +** Returns status +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data); + +/******************************************************************************* +** +** Function BTM_BleReset +** +** Description This function is called to reset ULP controller. +** +** Parameters None. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_BleReset(void); + +/******************************************************************************* +** +** Function BTM_BleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT8 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb); + + +/******************************************************************************* +** +** Function BTM_GetDeviceIDRoot +** +** Description This function is called to read the local device identity +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +BTM_API extern void BTM_GetDeviceIDRoot (BT_OCTET16 ir); + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +BTM_API extern void BTM_GetDeviceEncRoot (BT_OCTET16 er); + +/******************************************************************************* +** +** Function BTM_GetDeviceDHK +** +** Description This function is called to read the local device DHK. +** +** Returns void +** the local device DHK is copied into dhk +** +*******************************************************************************/ +BTM_API extern void BTM_GetDeviceDHK (BT_OCTET16 dhk); + +/******************************************************************************* +** +** Function BTM_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation BTM_SUCCESS if success. +** Otherwise, BTM_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +BTM_API extern void BTM_SecurityGrant(BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function BTM_BlePasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation SMP_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +BTM_API extern void BTM_BlePasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey); + +/******************************************************************************* +** +** Function BTM_LeOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +BTM_API extern void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data); + + +/******************************************************************************* +** +** Function BTM_BleDataSignature +** +** Description This function is called to sign the data using AES128 CMAC +** algorith. +** +** Parameter bd_addr: target device the data to be signed for. +** p_text: singing data +** len: length of the signing data +** signature: output parameter where data signature is going to +** be stored. +** +** Returns TRUE if signing sucessul, otherwise FALSE. +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature); + +/******************************************************************************* +** +** Function BTM_BleVerifySignature +** +** Description This function is called to verify the data signature +** +** Parameter bd_addr: target device the data to be signed for. +** p_orig: original data before signature. +** len: length of the signing data +** counter: counter used when doing data signing +** p_comp: signature to be compared against. + +** Returns TRUE if signature verified correctly; otherwise FALSE. +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_BleVerifySignature (BD_ADDR bd_addr, UINT8 *p_orig, + UINT16 len, UINT32 counter, + UINT8 *p_comp); + +/******************************************************************************* +** +** Function BTM_SetRandomAddr +** +** Description This function is called to set the local device random address +** . +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_SetRandomAddr (tBTM_BLE_RANDOM_SET_CBACK *p_cback); + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to set the local device random address +** . +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_ReadConnectionAddr (BD_ADDR conn_addr); + +/******************************************************************************* +** +** Function BTM_BleLoadLocalKeys +** +** Description Local local identity key, encryption root or sign counter. +** +** Parameters: key_type: type of key, can be BTM_BLE_KEY_TYPE_ID, BTM_BLE_KEY_TYPE_ER +** or BTM_BLE_KEY_TYPE_COUNTER. +** p_key: pointer to the key. +* +** Returns non2. +** +*******************************************************************************/ +BTM_API extern void BTM_BleLoadLocalKeys(UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key); + + +/******************************************************************************* +** +** Function BTM_BleSetBgConnType +** +** Description This function is called to set BLE background connection +** procedure type. It can be auto connection, or selective connection. +** +** Parameters conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE conn_type, + tBTM_BLE_SEL_CBACK *p_select_cback); + +/******************************************************************************* +** +** Function BTM_BleUpdateBgConnDev +** +** Description This function is called to add or remove a device into/from +** background connection procedure. The background connection +* procedure is decided by the background connection type, it can be +* auto connection, or selective connection. +** +** Parameters add_remove: TRUE to add; FALSE to remove. +** remote_bda: device address to add/remove. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR remote_bda); + + +/******************************************************** +** +** Function BTM_BleSetPrefConnParams +** +** Description Set a peripheral's preferred connection parameters. When +** any of the value does not want to be updated while others +** do, use BTM_BLE_CONN_PARAM_UNDEF for the ones want to +** leave untouched. +** +** Parameters: bd_addr - BD address of the peripheral +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_BleSetPrefConnParams (BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout); + +/****************************************************************************** +** +** Function BTM_BleSetConnScanParams +** +** Description Set scan parameters used in BLE connection request +** +** Parameters: scan_interval - scan interval +** scan_window - scan window +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_BleSetConnScanParams (UINT16 scan_interval, UINT16 scan_window); + +/******************************************************************************* +** +** Function BTM_CheckAdvData +** +** Description This function is called to get ADV data for a specific type. +** +** Parameters p_adv - pointer of ADV data +** type - finding ADV data type +** p_length - return the length of ADV data not including type +** +** Returns pointer of ADV data +** +*******************************************************************************/ +BTM_API extern UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT8 type, UINT8 *p_length); + +/******************************************************************************* +** +** Function BTM_ReadDevInfo +** +** Description This function is called to read the device/address type +** of BD address. +** +** Parameter remote_bda: remote device address +** p_dev_type: output parameter to read the device type. +** p_addr_type: output parameter to read the address type. +** +*******************************************************************************/ +BTM_API extern void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, + tBLE_ADDR_TYPE *p_addr_type); + +/******************************************************************************* +** +** Function BTM_BleBroadcast +** +** Description This function is to start or stop broadcasting. +** +** Parameters start: start or stop broadcasting. +** +** Returns status. +** +*******************************************************************************/ +BTM_API extern tBTM_STATUS BTM_BleBroadcast(BOOLEAN start); + +/******************************************************************************* +** +** Function BTM_RegisterScanReqEvt +** +** Description This function is called to register a scan request callback +** on the advertiser. +** +** Parameters p_scan_req_cback: scan request callback. If NULL, remove the +** registration. +** +** Returns void +** +*******************************************************************************/ +BTM_API extern void BTM_RegisterScanReqEvt(tBTM_BLE_SCAN_REQ_CBACK *p_scan_req_cback); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/include/btu.h b/stack/include/btu.h new file mode 100644 index 0000000..d33f0bb --- /dev/null +++ b/stack/include/btu.h @@ -0,0 +1,310 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Upper Layer definitions. The Broadcom + * implementations of L2CAP RFCOMM, SDP and the BTIf run as one GKI task. The + * btu_task switches between them. + * + ******************************************************************************/ + +#ifndef BTU_H +#define BTU_H + +#include "bt_target.h" +#include "gki.h" + +/* Define the BTU mailbox usage +*/ +#define BTU_HCI_RCV_MBOX TASK_MBOX_0 /* Messages from HCI */ +#define BTU_BTIF_MBOX TASK_MBOX_1 /* Messages to BTIF */ + +/* callbacks +*/ +typedef void (*tBTU_TIMER_CALLBACK)(TIMER_LIST_ENT *p_tle); +typedef void (*tBTU_EVENT_CALLBACK)(BT_HDR *p_hdr); + + +/* Define the timer types maintained by BTU +*/ +#define BTU_TTYPE_BTM_DEV_CTL 1 +#define BTU_TTYPE_L2CAP_LINK 2 +#define BTU_TTYPE_L2CAP_CHNL 3 +#define BTU_TTYPE_L2CAP_HOLD 4 +#define BTU_TTYPE_SDP 5 +#define BTU_TTYPE_BTM_SCO 6 +#define BTU_TTYPE_BTM_ACL 9 +#define BTU_TTYPE_BTM_RMT_NAME 10 +#define BTU_TTYPE_RFCOMM_MFC 11 +#define BTU_TTYPE_RFCOMM_PORT 12 +#define BTU_TTYPE_TCS_L2CAP 13 +#define BTU_TTYPE_TCS_CALL 14 +#define BTU_TTYPE_TCS_WUG 15 +#define BTU_TTYPE_AUTO_SYNC 16 +#define BTU_TTYPE_CTP_RECON 17 +#define BTU_TTYPE_CTP_T100 18 +#define BTU_TTYPE_CTP_GUARD 19 +#define BTU_TTYPE_CTP_DETACH 20 + +#define BTU_TTYPE_SPP_CONN_RETRY 21 +#define BTU_TTYPE_USER_FUNC 22 + +#define BTU_TTYPE_FTP_DISC 25 +#define BTU_TTYPE_OPP_DISC 26 + +#define BTU_TTYPE_CTP_TL_DISCVY 28 +#define BTU_TTYPE_IPFRAG_TIMER 29 +#define BTU_TTYPE_HSP2_AT_CMD_TO 30 +#define BTU_TTYPE_HSP2_REPEAT_RING 31 + +#define BTU_TTYPE_CTP_GW_INIT 32 +#define BTU_TTYPE_CTP_GW_CONN 33 +#define BTU_TTYPE_CTP_GW_IDLE 35 + +#define BTU_TTYPE_ICP_L2CAP 36 +#define BTU_TTYPE_ICP_T100 37 + +#define BTU_TTYPE_HSP2_WAIT_OK 38 + +/* HCRP Timers */ +#define BTU_TTYPE_HCRP_NOTIF_REG 39 +#define BTU_TTYPE_HCRP_PROTO_RSP 40 +#define BTU_TTYPE_HCRP_CR_GRANT 41 +#define BTU_TTYPE_HCRP_CR_CHECK 42 +#define BTU_TTYPE_HCRP_W4_CLOSE 43 + +/* HCRPM Timers */ +#define BTU_TTYPE_HCRPM_NOTIF_REG 44 +#define BTU_TTYPE_HCRPM_NOTIF_KEEP 45 +#define BTU_TTYPE_HCRPM_API_RSP 46 +#define BTU_TTYPE_HCRPM_W4_OPEN 47 +#define BTU_TTYPE_HCRPM_W4_CLOSE 48 + +/* BNEP Timers */ +#define BTU_TTYPE_BNEP 50 + +/* OBX */ +#define BTU_TTYPE_OBX_CLIENT_TO 51 +#define BTU_TTYPE_OBX_SERVER_TO 52 +#define BTU_TTYPE_OBX_SVR_SESS_TO 53 + + +#define BTU_TTYPE_HSP2_SDP_FAIL_TO 55 +#define BTU_TTYPE_HSP2_SDP_RTRY_TO 56 + +/* BTU internal */ +/* unused 60 */ + +#define BTU_TTYPE_AVDT_CCB_RET 61 +#define BTU_TTYPE_AVDT_CCB_RSP 62 +#define BTU_TTYPE_AVDT_CCB_IDLE 63 +#define BTU_TTYPE_AVDT_SCB_TC 64 + +#define BTU_TTYPE_HID_DEV_REPAGE_TO 65 +#define BTU_TTYPE_HID_HOST_REPAGE_TO 66 + +#define BTU_TTYPE_HSP2_DELAY_CKPD_RCV 67 + +#define BTU_TTYPE_SAP_TO 68 + +/* BPP Timer */ +#define BTU_TTYPE_BPP_REF_CHNL 72 + +/* LP HC idle Timer */ +#define BTU_TTYPE_LP_HC_IDLE_TO 74 + +/* Patch RAM Timer */ +#define BTU_TTYPE_PATCHRAM_TO 75 + +/* eL2CAP Info Request and other proto cmds timer */ +#define BTU_TTYPE_L2CAP_FCR_ACK 78 +#define BTU_TTYPE_L2CAP_INFO 79 + +/* BTU internal for BR/EDR and AMP HCI command timeout (reserve up to 3 AMP controller) */ +#define BTU_TTYPE_BTU_CMD_CMPL 80 +#define BTU_TTYPE_BTU_AMP1_CMD_CMPL 81 +#define BTU_TTYPE_BTU_AMP2_CMD_CMPL 82 +#define BTU_TTYPE_BTU_AMP3_CMD_CMPL 83 + +#define BTU_TTYPE_MCA_CCB_RSP 98 + +/* BTU internal timer for BLE activity */ +#define BTU_TTYPE_BLE_INQUIRY 99 +#define BTU_TTYPE_BLE_GAP_LIM_DISC 100 +#define BTU_TTYPE_ATT_WAIT_FOR_RSP 101 +#define BTU_TTYPE_SMP_PAIRING_CMD 102 +#define BTU_TTYPE_BLE_RANDOM_ADDR 103 +#define BTU_TTYPE_ATT_WAIT_FOR_APP_RSP 104 +#define BTU_TTYPE_ATT_WAIT_FOR_IND_ACK 105 +#define BTU_TTYPE_BLE_SCAN_PARAM_IDLE 106 + +/* Define the BTU_TASK APPL events +*/ +#if (defined(NFC_SHARED_TRANSPORT_ENABLED) && (NFC_SHARED_TRANSPORT_ENABLED==TRUE)) +#define BTU_NFC_AVAILABLE_EVT EVENT_MASK(APPL_EVT_0) /* Notifies BTU task that NFC is available (used for shared NFC+BT transport) */ +#endif + +/* This is the inquiry response information held by BTU, and available +** to applications. +*/ +typedef struct +{ + BD_ADDR remote_bd_addr; + UINT8 page_scan_rep_mode; + UINT8 page_scan_per_mode; + UINT8 page_scan_mode; + DEV_CLASS dev_class; + UINT16 clock_offset; +} tBTU_INQ_INFO; + + + +#define BTU_MAX_REG_TIMER (2) /* max # timer callbacks which may register */ +#define BTU_MAX_REG_EVENT (6) /* max # event callbacks which may register */ +#define BTU_DEFAULT_DATA_SIZE (0x2a0) + +#if (BLE_INCLUDED == TRUE) +#define BTU_DEFAULT_BLE_DATA_SIZE (27) +#endif + +/* structure to hold registered timers */ +typedef struct +{ + TIMER_LIST_ENT *p_tle; /* timer entry */ + tBTU_TIMER_CALLBACK timer_cb; /* callback triggered when timer expires */ +} tBTU_TIMER_REG; + +/* structure to hold registered event callbacks */ +typedef struct +{ + UINT16 event_range; /* start of event range */ + tBTU_EVENT_CALLBACK event_cb; /* callback triggered when event is in range */ +} tBTU_EVENT_REG; + +#define NFC_MAX_LOCAL_CTRLS 0 + +/* the index to BTU command queue array */ +#define NFC_CONTROLLER_ID (1) +#define BTU_MAX_LOCAL_CTRLS (1 + NFC_MAX_LOCAL_CTRLS) /* only BR/EDR */ + +/* AMP HCI control block */ +typedef struct +{ + BUFFER_Q cmd_xmit_q; + BUFFER_Q cmd_cmpl_q; + UINT16 cmd_window; + TIMER_LIST_ENT cmd_cmpl_timer; /* Command complete timer */ +#if (defined(BTU_CMD_CMPL_TOUT_DOUBLE_CHECK) && BTU_CMD_CMPL_TOUT_DOUBLE_CHECK == TRUE) + BOOLEAN checked_hcisu; +#endif +} tHCI_CMD_CB; + +/* Define structure holding BTU variables +*/ +typedef struct +{ + tBTU_TIMER_REG timer_reg[BTU_MAX_REG_TIMER]; + tBTU_EVENT_REG event_reg[BTU_MAX_REG_EVENT]; + + TIMER_LIST_Q quick_timer_queue; /* Timer queue for transport level (100/10 msec)*/ + TIMER_LIST_Q timer_queue; /* Timer queue for normal BTU task (1 second) */ + + TIMER_LIST_ENT cmd_cmpl_timer; /* Command complete timer */ + + UINT16 hcit_acl_data_size; /* Max ACL data size across HCI transport */ + UINT16 hcit_acl_pkt_size; /* Max ACL packet size across HCI transport */ + /* (this is data size plus 4 bytes overhead) */ + +#if BLE_INCLUDED == TRUE + UINT16 hcit_ble_acl_data_size; /* Max BLE ACL data size across HCI transport */ + UINT16 hcit_ble_acl_pkt_size; /* Max BLE ACL packet size across HCI transport */ + /* (this is data size plus 4 bytes overhead) */ +#endif + + BOOLEAN reset_complete; /* TRUE after first ack from device received */ + UINT8 trace_level; /* Trace level for HCI layer */ + + tHCI_CMD_CB hci_cmd_cb[BTU_MAX_LOCAL_CTRLS]; /* including BR/EDR */ +} tBTU_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global BTU data */ +#if BTU_DYNAMIC_MEMORY == FALSE +BTU_API extern tBTU_CB btu_cb; +#else +BTU_API extern tBTU_CB *btu_cb_ptr; +#define btu_cb (*btu_cb_ptr) +#endif + +BTU_API extern const BD_ADDR BT_BD_ANY; + +/* Functions provided by btu_task.c +************************************ +*/ +BTU_API extern void btu_start_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout); +BTU_API extern void btu_stop_timer (TIMER_LIST_ENT *p_tle); +BTU_API extern void btu_register_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout, tBTU_TIMER_CALLBACK timer_cb); +BTU_API extern void btu_deregister_timer(TIMER_LIST_ENT *p_tle); +BTU_API extern UINT32 btu_remaining_time (TIMER_LIST_ENT *p_tle); + + +BTU_API extern void btu_register_event_range (UINT16 range, tBTU_EVENT_CALLBACK event_cb); +BTU_API extern void btu_deregister_event_range (UINT16 range); +BTU_API extern void btu_uipc_rx_cback(BT_HDR *p_msg); + +BTU_API extern void btu_hcif_flush_cmd_queue(void); +/* +** Quick Timer +*/ +#if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) +#define QUICK_TIMER_TICKS (GKI_SECS_TO_TICKS (1)/QUICK_TIMER_TICKS_PER_SEC) +BTU_API extern void btu_start_quick_timer (TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout); +BTU_API extern void btu_stop_quick_timer (TIMER_LIST_ENT *p_tle); +BTU_API extern void btu_process_quick_timer_evt (void); +BTU_API extern void process_quick_timer_evt (TIMER_LIST_Q *p_tlq); +#endif + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) +BTU_API extern void btu_check_bt_sleep (void); +#endif + +/* Functions provided by btu_hcif.c +************************************ +*/ +BTU_API extern void btu_hcif_process_event (UINT8 controller_id, BT_HDR *p_buf); +BTU_API extern void btu_hcif_send_cmd (UINT8 controller_id, BT_HDR *p_msg); +BTU_API extern void btu_hcif_send_host_rdy_for_data(void); +BTU_API extern void btu_hcif_cmd_timeout (UINT8 controller_id); + +/* Functions provided by btu_core.c +************************************ +*/ +BTU_API extern void btu_init_core(void); +BTU_API extern void BTE_Init(void); +BTU_API extern UINT16 BTU_AclPktSize(void); +BTU_API extern UINT16 BTU_BleAclPktSize(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/include/dyn_mem.h b/stack/include/dyn_mem.h new file mode 100644 index 0000000..3ad8361 --- /dev/null +++ b/stack/include/dyn_mem.h @@ -0,0 +1,187 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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. + * + ******************************************************************************/ +#ifndef DYN_MEM_H +#define DYN_MEM_H + +/**************************************************************************** +** Define memory usage for GKI (if not defined in bdroid_buildcfg.h) +** The default for GKI is to use static memory allocation for its control +** block. +*/ +#ifndef GKI_DYNAMIC_MEMORY +#define GKI_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for each CORE component (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef BTU_DYNAMIC_MEMORY +#define BTU_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BTM_DYNAMIC_MEMORY +#define BTM_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SDP_DYNAMIC_MEMORY +#define SDP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef L2C_DYNAMIC_MEMORY +#define L2C_DYNAMIC_MEMORY FALSE +#endif + +#ifndef RFC_DYNAMIC_MEMORY +#define RFC_DYNAMIC_MEMORY FALSE +#endif + +#ifndef TCS_DYNAMIC_MEMORY +#define TCS_DYNAMIC_MEMORY FALSE +#endif + +#ifndef OBX_DYNAMIC_MEMORY +#define OBX_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BNEP_DYNAMIC_MEMORY +#define BNEP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVDT_DYNAMIC_MEMORY +#define AVDT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVCT_DYNAMIC_MEMORY +#define AVCT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef MCA_DYNAMIC_MEMORY +#define MCA_DYNAMIC_MEMORY FALSE +#endif + +#ifndef GATT_DYNAMIC_MEMORY +#define GATT_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SMP_DYNAMIC_MEMORY +#define SMP_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for each PROFILE component (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef A2D_DYNAMIC_MEMORY +#define A2D_DYNAMIC_MEMORY FALSE +#endif + +#ifndef VDP_DYNAMIC_MEMORY +#define VDP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef AVRC_DYNAMIC_MEMORY +#define AVRC_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BIP_DYNAMIC_MEMORY +#define BIP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef BPP_DYNAMIC_MEMORY +#define BPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef CTP_DYNAMIC_MEMORY +#define CTP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef DUN_DYNAMIC_MEMORY +#define DUN_DYNAMIC_MEMORY FALSE +#endif + +#ifndef FTP_DYNAMIC_MEMORY +#define FTP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef GAP_DYNAMIC_MEMORY +#define GAP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef GOEP_DYNAMIC_MEMORY +#define GOEP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HCRP_DYNAMIC_MEMORY +#define HCRP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HFP_DYNAMIC_MEMORY +#define HFP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HID_DYNAMIC_MEMORY +#define HID_DYNAMIC_MEMORY FALSE +#endif + +#ifndef HSP2_DYNAMIC_MEMORY +#define HSP2_DYNAMIC_MEMORY FALSE +#endif + +#ifndef ICP_DYNAMIC_MEMORY +#define ICP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef OPP_DYNAMIC_MEMORY +#define OPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef PAN_DYNAMIC_MEMORY +#define PAN_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SPP_DYNAMIC_MEMORY +#define SPP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef SLIP_DYNAMIC_MEMORY +#define SLIP_DYNAMIC_MEMORY FALSE +#endif + +#ifndef LLCP_DYNAMIC_MEMORY +#define LLCP_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for BTA (if not defined in bdroid_buildcfg.h) +** The default for each component is to use static memory allocations. +*/ +#ifndef BTA_DYNAMIC_MEMORY +#define BTA_DYNAMIC_MEMORY FALSE +#endif + +/**************************************************************************** +** Define memory usage for BT Trace (if not defined in bdroid_buildcfg.h) +** The default is to use static memory allocations. +*/ +#ifndef BTTRC_DYNAMIC_MEMORY +#define BTTRC_DYNAMIC_MEMORY FALSE +#endif + +#endif /* #ifdef DYN_MEM_H */ + diff --git a/stack/include/gatt_api.h b/stack/include/gatt_api.h new file mode 100644 index 0000000..2d13e5a --- /dev/null +++ b/stack/include/gatt_api.h @@ -0,0 +1,1128 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef GATT_API_H +#define GATT_API_H + +#include "bt_target.h" +#include "gattdefs.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ +/* Success code and error codes */ +#define GATT_SUCCESS 0x0000 +#define GATT_INVALID_HANDLE 0x0001 +#define GATT_READ_NOT_PERMIT 0x0002 +#define GATT_WRITE_NOT_PERMIT 0x0003 +#define GATT_INVALID_PDU 0x0004 +#define GATT_INSUF_AUTHENTICATION 0x0005 +#define GATT_REQ_NOT_SUPPORTED 0x0006 +#define GATT_INVALID_OFFSET 0x0007 +#define GATT_INSUF_AUTHORIZATION 0x0008 +#define GATT_PREPARE_Q_FULL 0x0009 +#define GATT_NOT_FOUND 0x000a +#define GATT_NOT_LONG 0x000b +#define GATT_INSUF_KEY_SIZE 0x000c +#define GATT_INVALID_ATTR_LEN 0x000d +#define GATT_ERR_UNLIKELY 0x000e +#define GATT_INSUF_ENCRYPTION 0x000f +#define GATT_UNSUPPORT_GRP_TYPE 0x0010 +#define GATT_INSUF_RESOURCE 0x0011 + + +#define GATT_ILLEGAL_PARAMETER 0x0087 +#define GATT_NO_RESOURCES 0x0080 +#define GATT_INTERNAL_ERROR 0x0081 +#define GATT_WRONG_STATE 0x0082 +#define GATT_DB_FULL 0x0083 +#define GATT_BUSY 0x0084 +#define GATT_ERROR 0x0085 +#define GATT_CMD_STARTED 0x0086 +#define GATT_PENDING 0x0088 +#define GATT_AUTH_FAIL 0x0089 +#define GATT_MORE 0x008a +#define GATT_INVALID_CFG 0x008b +#define GATT_SERVICE_STARTED 0x008c +#define GATT_ENCRYPED_MITM GATT_SUCCESS +#define GATT_ENCRYPED_NO_MITM 0x008d +#define GATT_NOT_ENCRYPTED 0x008e + + +typedef UINT8 tGATT_STATUS; + + +#define GATT_RSP_ERROR 0x01 +#define GATT_REQ_MTU 0x02 +#define GATT_RSP_MTU 0x03 +#define GATT_REQ_FIND_INFO 0x04 +#define GATT_RSP_FIND_INFO 0x05 +#define GATT_REQ_FIND_TYPE_VALUE 0x06 +#define GATT_RSP_FIND_TYPE_VALUE 0x07 +#define GATT_REQ_READ_BY_TYPE 0x08 +#define GATT_RSP_READ_BY_TYPE 0x09 +#define GATT_REQ_READ 0x0A +#define GATT_RSP_READ 0x0B +#define GATT_REQ_READ_BLOB 0x0C +#define GATT_RSP_READ_BLOB 0x0D +#define GATT_REQ_READ_MULTI 0x0E +#define GATT_RSP_READ_MULTI 0x0F +#define GATT_REQ_READ_BY_GRP_TYPE 0x10 +#define GATT_RSP_READ_BY_GRP_TYPE 0x11 +#define GATT_REQ_WRITE 0x12 /* 0001-0010 (write)*/ +#define GATT_RSP_WRITE 0x13 +#define GATT_CMD_WRITE 0x52 /* changed in V4.0 01001-0010(write cmd)*/ +#define GATT_REQ_PREPARE_WRITE 0x16 +#define GATT_RSP_PREPARE_WRITE 0x17 +#define GATT_REQ_EXEC_WRITE 0x18 +#define GATT_RSP_EXEC_WRITE 0x19 +#define GATT_HANDLE_VALUE_NOTIF 0x1B +#define GATT_HANDLE_VALUE_IND 0x1D +#define GATT_HANDLE_VALUE_CONF 0x1E +#define GATT_SIGN_CMD_WRITE 0xD2 /* changed in V4.0 1101-0010 (signed write) see write cmd above*/ +#define GATT_OP_CODE_MAX GATT_HANDLE_VALUE_CONF + 1 /* 0x1E = 30 + 1 = 31*/ + + +#define GATT_HANDLE_IS_VALID(x) ((x) != 0) + +#define GATT_CONN_UNKNOWN 0 +#define GATT_CONN_NO_RESOURCES L2CAP_CONN_NO_RESOURCES /* connection fail for l2cap resource failure */ +#define GATT_CONN_TIMEOUT HCI_ERR_CONNECTION_TOUT /* 0x08 connection timeout */ +#define GATT_CONN_TERMINATE_PEER_USER HCI_ERR_PEER_USER /* 0x13 connection terminate by peer user */ +#define GATT_CONN_TERMINATE_LOCAL_HOST HCI_ERR_CONN_CAUSE_LOCAL_HOST /* 0x16 connectionterminated by local host */ +#define GATT_CONN_FAIL_ESTABLISH HCI_ERR_CONN_FAILED_ESTABLISHMENT/* 0x03E connection fail to establish */ +#define GATT_CONN_LMP_TIMEOUT HCI_ERR_LMP_RESPONSE_TIMEOUT /* 0x22 connection fail for LMP response tout */ +#define GATT_CONN_CANCEL L2CAP_CONN_CANCEL /* 0x0100 L2CAP connection cancelled */ +typedef UINT16 tGATT_DISCONN_REASON; + +/* MAX GATT MTU size +*/ +#ifndef GATT_MAX_MTU_SIZE + #define GATT_MAX_MTU_SIZE 517 +#endif + +/* max legth of an attribute value +*/ +#ifndef GATT_MAX_ATTR_LEN + #define GATT_MAX_ATTR_LEN 600 +#endif + +/* default GATT MTU size over LE link +*/ +#define GATT_DEF_BLE_MTU_SIZE 23 + +/* invalid connection ID +*/ +#define GATT_INVALID_CONN_ID 0xFFFF + +#ifndef GATT_CL_MAX_LCB + #define GATT_CL_MAX_LCB 22 +#endif + +#ifndef GATT_MAX_SCCB + #define GATT_MAX_SCCB 10 +#endif + + + +/* GATT notification caching timer, default to be three seconds +*/ +#ifndef GATTC_NOTIF_TIMEOUT + #define GATTC_NOTIF_TIMEOUT 3 +#endif + +/***************************************************************************** +** GATT Structure Definition +*****************************************************************************/ + +/* Attribute permissions +*/ +#define GATT_PERM_READ (1 << 0) /* bit 0 */ +#define GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 */ +#define GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 */ +#define GATT_PERM_WRITE (1 << 4) /* bit 4 */ +#define GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 */ +#define GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 */ +#define GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 */ +#define GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 */ +typedef UINT16 tGATT_PERM; + +#define GATT_ENCRYPT_KEY_SIZE_MASK (0xF000) /* the MS nibble of tGATT_PERM; key size 7=0; size 16=9 */ + +#define GATT_READ_ALLOWED (GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) +#define GATT_READ_AUTH_REQUIRED (GATT_PERM_READ_ENC_MITM) +#define GATT_READ_MITM_REQUIRED (GATT_PERM_READ_ENC_MITM) +#define GATT_READ_ENCRYPTED_REQUIRED (GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ_ENC_MITM) + + +#define GATT_WRITE_ALLOWED (GATT_PERM_WRITE | GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM | \ + GATT_PERM_WRITE_SIGNED | GATT_PERM_WRITE_SIGNED_MITM) + +#define GATT_WRITE_AUTH_REQUIRED (GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_SIGNED) + +#define GATT_WRITE_MITM_REQUIRED (GATT_PERM_WRITE_ENC_MITM | GATT_PERM_WRITE_SIGNED_MITM) + +#define GATT_WRITE_ENCRYPTED_PERM (GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE_ENC_MITM) + +#define GATT_WRITE_SIGNED_PERM (GATT_PERM_WRITE_SIGNED | GATT_PERM_WRITE_SIGNED_MITM) + + +/* Characteristic properties +*/ +#define GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) +#define GATT_CHAR_PROP_BIT_READ (1 << 1) +#define GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) +#define GATT_CHAR_PROP_BIT_WRITE (1 << 3) +#define GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) +#define GATT_CHAR_PROP_BIT_INDICATE (1 << 5) +#define GATT_CHAR_PROP_BIT_AUTH (1 << 6) +#define GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) +typedef UINT8 tGATT_CHAR_PROP; + + +/* Format of the value of a characteristic. enumeration type +*/ +enum +{ + GATT_FORMAT_RES, /* rfu */ + GATT_FORMAT_BOOL, /* 0x01 boolean */ + GATT_FORMAT_2BITS, /* 0x02 2 bit */ + GATT_FORMAT_NIBBLE, /* 0x03 nibble */ + GATT_FORMAT_UINT8, /* 0x04 uint8 */ + GATT_FORMAT_UINT12, /* 0x05 uint12 */ + GATT_FORMAT_UINT16, /* 0x06 uint16 */ + GATT_FORMAT_UINT24, /* 0x07 uint24 */ + GATT_FORMAT_UINT32, /* 0x08 uint32 */ + GATT_FORMAT_UINT48, /* 0x09 uint48 */ + GATT_FORMAT_UINT64, /* 0x0a uint64 */ + GATT_FORMAT_UINT128, /* 0x0B uint128 */ + GATT_FORMAT_SINT8, /* 0x0C signed 8 bit integer */ + GATT_FORMAT_SINT12, /* 0x0D signed 12 bit integer */ + GATT_FORMAT_SINT16, /* 0x0E signed 16 bit integer */ + GATT_FORMAT_SINT24, /* 0x0F signed 24 bit integer */ + GATT_FORMAT_SINT32, /* 0x10 signed 32 bit integer */ + GATT_FORMAT_SINT48, /* 0x11 signed 48 bit integer */ + GATT_FORMAT_SINT64, /* 0x12 signed 64 bit integer */ + GATT_FORMAT_SINT128, /* 0x13 signed 128 bit integer */ + GATT_FORMAT_FLOAT32, /* 0x14 float 32 */ + GATT_FORMAT_FLOAT64, /* 0x15 float 64*/ + GATT_FORMAT_SFLOAT, /* 0x16 IEEE-11073 16 bit SFLOAT */ + GATT_FORMAT_FLOAT, /* 0x17 IEEE-11073 32 bit SFLOAT */ + GATT_FORMAT_DUINT16, /* 0x18 IEEE-20601 format */ + GATT_FORMAT_UTF8S, /* 0x19 UTF-8 string */ + GATT_FORMAT_UTF16S, /* 0x1a UTF-16 string */ + GATT_FORMAT_STRUCT, /* 0x1b Opaque structure*/ + GATT_FORMAT_MAX /* 0x1c or above reserved */ +}; +typedef UINT8 tGATT_FORMAT; + +/* Characteristic Presentation Format Descriptor value +*/ +typedef struct +{ + UINT16 unit; /* as UUIUD defined by SIG */ + UINT16 descr; /* as UUID as defined by SIG */ + tGATT_FORMAT format; + INT8 exp; + UINT8 name_spc; /* The name space of the description */ +} tGATT_CHAR_PRES; + +#define GATT_VALID_RANGE_MAX_SIZE 16 +typedef struct +{ + UINT8 format; + UINT16 len; + UINT8 lower_range[GATT_VALID_RANGE_MAX_SIZE]; /* in little endian format */ + UINT8 upper_range[GATT_VALID_RANGE_MAX_SIZE]; +} tGATT_VALID_RANGE; + +/* Characteristic Aggregate Format attribute value +*/ +#define GATT_AGGR_HANDLE_NUM_MAX 10 +typedef struct +{ + UINT8 num_handle; + UINT16 handle_list[GATT_AGGR_HANDLE_NUM_MAX]; +} tGATT_CHAR_AGGRE; + +/* Characteristic descriptor: Extended Properties value +*/ +#define GATT_CHAR_BIT_REL_WRITE 0x0001 /* permits reliable writes of the Characteristic Value */ +#define GATT_CHAR_BIT_WRITE_AUX 0x0002 /* permits writes to the characteristic descriptor */ + + +/* characteristic descriptor: client configuration value +*/ +#define GATT_CLT_CONFIG_NONE 0x0000 +#define GATT_CLT_CONFIG_NOTIFICATION 0x0001 +#define GATT_CLT_CONFIG_INDICATION 0x0002 +typedef UINT16 tGATT_CLT_CHAR_CONFIG; + + +/* characteristic descriptor: server configuration value +*/ +#define GATT_SVR_CONFIG_NONE 0x0000 +#define GATT_SVR_CONFIG_BROADCAST 0x0001 +typedef UINT16 tGATT_SVR_CHAR_CONFIG; + +/* Characteristic descriptor: Extended Properties value +*/ +#define GATT_CHAR_BIT_REL_WRITE 0x0001 /* permits reliable writes of the Characteristic Value */ +#define GATT_CHAR_BIT_WRITE_AUX 0x0002 /* permits writes to the characteristic descriptor */ + +/* authentication requirement +*/ +#define GATT_AUTH_REQ_NONE 0 +#define GATT_AUTH_REQ_NO_MITM 1 /* unauthenticated encryption */ +#define GATT_AUTH_REQ_MITM 2 /* authenticated encryption */ +#define GATT_AUTH_REQ_SIGNED_NO_MITM 3 +#define GATT_AUTH_REQ_SIGNED_MITM 4 +typedef UINT8 tGATT_AUTH_REQ; + +/* Attribute Value structure +*/ +typedef struct +{ + UINT16 conn_id; + UINT16 handle; /* attribute handle */ + UINT16 offset; /* attribute value offset, if no offfset is needed for the command, ignore it */ + UINT16 len; /* length of attribute value */ + tGATT_AUTH_REQ auth_req; /* authentication request */ + UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ +} tGATT_VALUE; + +/* Union of the event data which is used in the server respond API to carry the server response information +*/ +typedef union +{ + /* data type member event */ + tGATT_VALUE attr_value; /* READ, HANDLE_VALUE_IND, PREPARE_WRITE */ + /* READ_BLOB, READ_BY_TYPE */ + UINT16 handle; /* WRITE, WRITE_BLOB */ + +} tGATTS_RSP; + +/* Transports for the primary service */ +enum +{ + GATT_TRANSPORT_LE, + GATT_TRANSPORT_BR_EDR, + GATT_TRANSPORT_LE_BR_EDR +}; +typedef UINT8 tGATT_TRANSPORT; + +#define GATT_PREP_WRITE_CANCEL 0x00 +#define GATT_PREP_WRITE_EXEC 0x01 +typedef UINT8 tGATT_EXEC_FLAG; + +/* read request always based on UUID */ +typedef struct +{ + UINT16 handle; + UINT16 offset; + BOOLEAN is_long; +} tGATT_READ_REQ; + +/* write request data */ +typedef struct +{ + UINT16 handle; /* attribute handle */ + UINT16 offset; /* attribute value offset, if no offfset is needed for the command, ignore it */ + UINT16 len; /* length of attribute value */ + UINT8 value[GATT_MAX_ATTR_LEN]; /* the actual attribute value */ + BOOLEAN need_rsp; /* need write response */ + BOOLEAN is_prep; /* is prepare write */ +} tGATT_WRITE_REQ; + +/* callback data for server access request from client */ +typedef union +{ + tGATT_READ_REQ read_req; /* read request, read by Type, read blob */ + + tGATT_WRITE_REQ write_req; /* write */ + /* prepare write */ + /* write blob */ + UINT16 handle; /* handle value confirmation */ + UINT16 mtu; /* MTU exchange request */ + tGATT_EXEC_FLAG exec_write; /* execute write */ +} tGATTS_DATA; + +typedef UINT8 tGATT_SERV_IF; /* GATT Service Interface */ + +enum +{ + GATTS_REQ_TYPE_READ = 1, /* Attribute read request */ + GATTS_REQ_TYPE_WRITE, /* Attribute write request */ + GATTS_REQ_TYPE_WRITE_EXEC, /* Execute write */ + GATTS_REQ_TYPE_MTU, /* MTU exchange information */ + GATTS_REQ_TYPE_CONF /* handle value confirmation */ +}; +typedef UINT8 tGATTS_REQ_TYPE; + + + +/* Client Used Data Structure +*/ +/* definition of different discovery types */ +enum +{ + GATT_DISC_SRVC_ALL = 1, /* discover all services */ + GATT_DISC_SRVC_BY_UUID, /* discover service of a special type */ + GATT_DISC_INC_SRVC, /* discover the included service within a service */ + GATT_DISC_CHAR, /* discover characteristics of a service with/without type requirement */ + GATT_DISC_CHAR_DSCPT, /* discover characteristic descriptors of a character */ + GATT_DISC_MAX /* maximnun discover type */ +}; +typedef UINT8 tGATT_DISC_TYPE; + +/* Discover parameters of different discovery types +*/ +typedef struct +{ + tBT_UUID service; + UINT16 s_handle; + UINT16 e_handle; +}tGATT_DISC_PARAM; + +/* GATT read type enumeration +*/ +enum +{ + GATT_READ_BY_TYPE = 1, + GATT_READ_BY_HANDLE, + GATT_READ_MULTIPLE, + GATT_READ_CHAR_VALUE, + GATT_READ_PARTIAL, + GATT_READ_MAX +}; +typedef UINT8 tGATT_READ_TYPE; + +/* Read By Type Request (GATT_READ_BY_TYPE) Data +*/ +typedef struct +{ + tGATT_AUTH_REQ auth_req; + UINT16 s_handle; + UINT16 e_handle; + tBT_UUID uuid; +} tGATT_READ_BY_TYPE; + +/* GATT_READ_MULTIPLE request data +*/ +#define GATT_MAX_READ_MULTI_HANDLES 10 /* Max attributes to read in one request */ +typedef struct +{ + tGATT_AUTH_REQ auth_req; + UINT16 num_handles; /* number of handles to read */ + UINT16 handles[GATT_MAX_READ_MULTI_HANDLES]; /* handles list to be read */ +} tGATT_READ_MULTI; + +/* Read By Handle Request (GATT_READ_BY_HANDLE) data */ +typedef struct +{ + tGATT_AUTH_REQ auth_req; + UINT16 handle; +} tGATT_READ_BY_HANDLE; + +/* READ_BT_HANDLE_Request data */ +typedef struct +{ + tGATT_AUTH_REQ auth_req; + UINT16 handle; + UINT16 offset; +} tGATT_READ_PARTIAL; + +/* Read Request Data +*/ +typedef union +{ + tGATT_READ_BY_TYPE service; + tGATT_READ_BY_TYPE char_type; /* characterisitc type */ + tGATT_READ_MULTI read_multiple; + tGATT_READ_BY_HANDLE by_handle; + tGATT_READ_PARTIAL partial; +} tGATT_READ_PARAM; + +/* GATT write type enumeration */ +enum +{ + GATT_WRITE_NO_RSP = 1, + GATT_WRITE , + GATT_WRITE_PREPARE +}; +typedef UINT8 tGATT_WRITE_TYPE; + +/* Client Operation Complete Callback Data +*/ +typedef union +{ + tGATT_VALUE att_value; + UINT16 mtu; + UINT16 handle; +} tGATT_CL_COMPLETE; + +/* GATT client operation type, used in client callback function +*/ +#define GATTC_OPTYPE_NONE 0 +#define GATTC_OPTYPE_DISCOVERY 1 +#define GATTC_OPTYPE_READ 2 +#define GATTC_OPTYPE_WRITE 3 +#define GATTC_OPTYPE_EXE_WRITE 4 +#define GATTC_OPTYPE_CONFIG 5 +#define GATTC_OPTYPE_NOTIFICATION 6 +#define GATTC_OPTYPE_INDICATION 7 +typedef UINT8 tGATTC_OPTYPE; + +/* characteristic declaration +*/ +typedef struct +{ + tGATT_CHAR_PROP char_prop; /* characterisitc properties */ + UINT16 val_handle; /* characteristic value attribute handle */ + tBT_UUID char_uuid; /* characteristic UUID type */ +} tGATT_CHAR_DCLR_VAL; + +/* primary service group data +*/ +typedef struct +{ + UINT16 e_handle; /* ending handle of the group */ + tBT_UUID service_type; /* group type */ +} tGATT_GROUP_VALUE; + + +/* included service attribute value +*/ +typedef struct +{ + tBT_UUID service_type; /* included service UUID */ + UINT16 s_handle; /* starting handle */ + UINT16 e_handle; /* ending handle */ +} tGATT_INCL_SRVC; + +typedef union +{ + tGATT_INCL_SRVC incl_service; /* include service value */ + tGATT_GROUP_VALUE group_value; /* Service UUID type. + This field is used with GATT_DISC_SRVC_ALL + type of discovery result callback. */ + + UINT16 handle; /* When used with GATT_DISC_SRVC_BY_UUID type + discovery result, it is the ending handle of a + known service to be discovered. When used with + GATT_DISC_INC_SRVC type discovery result, + it is the included service starting handle.*/ + + tGATT_CHAR_DCLR_VAL dclr_value; /* Characteristic declaration value. + This field is used with GATT_DISC_CHAR type discovery.*/ +} tGATT_DISC_VALUE; + +/* discover result record +*/ +typedef struct +{ + tBT_UUID type; + UINT16 handle; + tGATT_DISC_VALUE value; +} tGATT_DISC_RES; + + +typedef UINT8 tGATT_IF; +#define GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP 0 /* start a idle timer for this duration when no application + need to use the link */ + +#define GATT_LINK_NO_IDLE_TIMEOUT 0xFFFF + +#define GATT_INVALID_ACL_HANDLE 0xFFFF +/* discover result callback function */ +typedef void (tGATT_DISC_RES_CB) (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); + +/* discover complete callback function */ +typedef void (tGATT_DISC_CMPL_CB) (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); + +/* Define a callback function for when read/write/disc/config operation is completed. */ +typedef void (tGATT_CMPL_CBACK) (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + +/* Define a callback function when an initialized connection is established. */ +typedef void (tGATT_CONN_CBACK) (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, tGATT_DISCONN_REASON reason); + +/* attribute request callback for ATT server */ +typedef void (tGATT_REQ_CBACK )(UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, tGATTS_DATA *p_data); + + + + +/* Define the structure that applications use to register with +** GATT. This structure includes callback functions. All functions +** MUST be provided. +*/ +typedef struct +{ + tGATT_CONN_CBACK *p_conn_cb; + tGATT_CMPL_CBACK *p_cmpl_cb; + tGATT_DISC_RES_CB *p_disc_res_cb; + tGATT_DISC_CMPL_CB *p_disc_cmpl_cb; + tGATT_REQ_CBACK *p_req_cb; +} tGATT_CBACK; + +/*********************** Start Handle Management Definitions ********************** +*/ + + +typedef struct +{ + tBT_UUID app_uuid128; + tBT_UUID svc_uuid; + UINT16 svc_inst; + UINT16 s_handle; + UINT16 e_handle; + BOOLEAN is_primary; /* primary service or secondary */ +} tGATTS_HNDL_RANGE; + + + +#define GATTS_SRV_CHG_CMD_ADD_CLIENT 1 +#define GATTS_SRV_CHG_CMD_UPDATE_CLIENT 2 +#define GATTS_SRV_CHG_CMD_REMOVE_CLIENT 3 +#define GATTS_SRV_CHG_CMD_READ_NUM_CLENTS 4 +#define GATTS_SRV_CHG_CMD_READ_CLENT 5 +typedef UINT8 tGATTS_SRV_CHG_CMD; + +typedef struct +{ + BD_ADDR bda; + BOOLEAN srv_changed; +} tGATTS_SRV_CHG; + + +typedef union +{ + tGATTS_SRV_CHG srv_chg; + UINT8 client_read_index; /* only used for sequential reading client srv chg info */ +} tGATTS_SRV_CHG_REQ; + +typedef union +{ + tGATTS_SRV_CHG srv_chg; + UINT8 num_clients; +} tGATTS_SRV_CHG_RSP; + + + +typedef struct +{ + tGATTS_HNDL_RANGE *p_new_srv_start; +} tGATTS_PENDING_NEW_SRV_START; + +/* Attibute server handle ranges NV storage callback functions +*/ +typedef void (tGATTS_NV_SAVE_CBACK)(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range); +typedef BOOLEAN (tGATTS_NV_SRV_CHG_CBACK)(tGATTS_SRV_CHG_CMD cmd, tGATTS_SRV_CHG_REQ *p_req, tGATTS_SRV_CHG_RSP *p_rsp); + +typedef struct +{ + tGATTS_NV_SAVE_CBACK *p_nv_save_callback; + tGATTS_NV_SRV_CHG_CBACK *p_srv_chg_callback; +} tGATT_APPL_INFO; + +/* +*********************** End Handle Management Definitions **********************/ + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function GATT_SetTraceLevel +** +** Description This function sets the trace level. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ + GATT_API extern UINT8 GATT_SetTraceLevel (UINT8 new_level); + + +/*******************************************************************************/ +/* GATT Profile API Functions */ +/*******************************************************************************/ +/* GATT Profile Server Functions */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function GATTS_AddHandleRange +** +** Description This function add the allocated handles range for the specifed +** application UUID, service UUID and service instance +** +** Parameter p_hndl_range: pointer to allocated handles information +** +** Returns TRUE if handle range is added sucessfully; otherwise FALSE. +** +*******************************************************************************/ + + GATT_API extern BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range); + +/******************************************************************************* +** +** Function GATTS_NVRegister +** +** Description Application manager calls this function to register for +** NV save callback function. There can be one and only one +** NV save callback function. +** +** Parameter p_cb_info : callback informaiton +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATTS_NVRegister (tGATT_APPL_INFO *p_cb_info); + + +/******************************************************************************* +** +** Function GATTS_CreateService +** +** Description This function is called to reserve a block of handles for a service. +** +** *** It should be called only once per service instance *** +** +** Parameter gatt_if : application if +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** num_handles : number of handles needed by the service. +** is_pri : is a primary service or not. +** +** Returns service handle if sucessful, otherwise 0. +** +*******************************************************************************/ + GATT_API extern UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst, UINT16 num_handles, BOOLEAN is_pri); + + +/******************************************************************************* +** +** Function GATTS_AddIncludeService +** +** Description This function is called to add an included service. +** +** Parameter service_handle : To which service this included service is added to. +** include_svc_handle : included service handle. +** +** Returns included service attribute handle. If 0, add included service +** fail. +** +*******************************************************************************/ + GATT_API extern UINT16 GATTS_AddIncludeService (UINT16 service_handle, + UINT16 include_svc_handle); + + +/******************************************************************************* +** +** Function GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** It will add a characteristic declaration and characteristic +** value declaration into the service database identified by the +** service handle. +** +** Parameter service_handle : To which service this included service is added to. +** char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns Characteristic value declaration attribute handle. 0 if add +** characteristic failed. +** +*******************************************************************************/ + GATT_API extern UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *char_uuid, + tGATT_PERM perm,tGATT_CHAR_PROP property); + +/******************************************************************************* +** +** Function GATTS_AddCharDescriptor +** +** Description This function is called to add a characteristic descriptor +** into a service database. Add descriptor should follow add char +** to which it belongs, and next add char should be done only +** after all add descriptors for the previous char. +** +** Parameter service_handle : To which service this characteristic descriptor +** is added to. +** perm : Characteristic value declaration attribute +** permission. +** p_descr_uuid : Characteristic descriptor UUID. +** +** Returns Characteristic descriptor attribute handle. 0 if add +** characteristic descriptor failed. +** +*******************************************************************************/ + GATT_API extern UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, tGATT_PERM perm, + tBT_UUID * p_descr_uuid); + +/******************************************************************************* +** +** Function GATTS_DeleteService +** +** Description This function is called to delete a service. +** +** Parameter gatt_if : application interface +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** +** Returns TRUE if operation succeed, FALSE if handle block was not found. +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_inst); + +/******************************************************************************* +** +** Function GATTS_StartService +** +** Description This function is called to start a service with GATT +** +** Parameter gatt_if : service handle. +** p_cback : application service callback functions. +** sup_transport : supported transport(s) for this primary service +** +** return GATT_SUCCESS if sucessfully started; otherwise error code. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, + tGATT_TRANSPORT sup_transport); + + +/******************************************************************************* +** +** Function GATTS_StopService +** +** Description This function is called to stop a service +** +** Parameter service_handle : this is the start handle of a service +** +** Returns None. +** +*******************************************************************************/ + GATT_API extern void GATTS_StopService (UINT16 service_handle); + + +/******************************************************************************* +** +** Function GATTs_HandleValueIndication +** +** Description This function sends a handle value indication to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if sucessfully sent or queued; otherwise error code. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, + UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val); + +/******************************************************************************* +** +** Function GATTS_HandleValueNotification +** +** Description This function sends a handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val); + + +/******************************************************************************* +** +** Function GATTS_SendRsp +** +** Description This function sends the server response to client. +** +** Parameter conn_id: connection identifier. +** trans_id: transaction id +** status: response status +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if sucessfully sent; otherwise error code. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tGATT_STATUS status, tGATTS_RSP *p_msg); + + +/*******************************************************************************/ +/* GATT Profile Client Functions */ +/*******************************************************************************/ + +/******************************************************************************* +** +** Function GATTC_ConfigureMTU +** +** Description This function is called to configure the ATT MTU size for +** a connection on an LE transport. +** +** Parameters conn_id: connection identifier. +** mtu - attribute MTU size.. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_ConfigureMTU (UINT16 conn_id, UINT16 mtu); + +/******************************************************************************* +** +** Function GATTC_Discover +** +** Description This function is called to do a discovery procedure on ATT server. +** +** Parameters conn_id: connection identifier. +** disc_type:discovery type. +** p_param: parameters of discovery requirement. +** +** Returns GATT_SUCCESS if command received/sent successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_Discover (UINT16 conn_id, + tGATT_DISC_TYPE disc_type, + tGATT_DISC_PARAM *p_param ); +/******************************************************************************* +** +** Function GATTC_Read +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute read type. +** p_read - read operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, + tGATT_READ_PARAM *p_read); + +/******************************************************************************* +** +** Function GATTC_Write +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute write type. +** p_write - write operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, + tGATT_VALUE *p_write); + + +/******************************************************************************* +** +** Function GATTC_ExecuteWrite +** +** Description This function is called to send an Execute write request to +** the server. +** +** Parameters conn_id: connection identifier. +** is_execute - to execute or cancel the prepare write requet(s) +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute); + +/******************************************************************************* +** +** Function GATTC_SendHandleValueConfirm +** +** Description This function is called to send a handle value confirmation +** as response to a handle value notification from server. +** +** Parameters conn_id: connection identifier. +** handle: the handle of the attribute confirmation. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle); + + +/******************************************************************************* +** +** Function GATT_SetIdleTimeout +** +** Description This function (common to both client and server) sets the idle +** timeout for a tansport connection +** +** Parameter bd_addr: target device bd address. +** idle_tout: timeout value in seconds. +** +** Returns void +** +*******************************************************************************/ + GATT_API extern void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout); + + +/******************************************************************************* +** +** Function GATT_Register +** +** Description This function is called to register an application +** with GATT +** +** Parameter p_app_uuid128: Application UUID +** p_cb_info: callback functions. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ + GATT_API extern tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, tGATT_CBACK *p_cb_info); + +/******************************************************************************* +** +** Function GATT_Deregister +** +** Description This function deregistered the application from GATT. +** +** Parameters gatt_if: applicaiton interface. +** +** Returns None. +** +*******************************************************************************/ + GATT_API extern void GATT_Deregister (tGATT_IF gatt_if); + +/******************************************************************************* +** +** Function GATT_StartIf +** +** Description This function is called after registration to start receiving +** callbacks for registered interface. Function may call back +** with connection status and queued notifications +** +** Parameter gatt_if: applicaiton interface. +** +** Returns None +** +*******************************************************************************/ + GATT_API extern void GATT_StartIf (tGATT_IF gatt_if); + +/******************************************************************************* +** +** Function GATT_Connect +** +** Description This function initiate a connecttion to a ATT server. +** +** Parameters gatt_if: applicaiton interface +** bd_addr: peer device address. +** is_direct: is a direct conenection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct); + + +/******************************************************************************* +** +** Function GATT_CancelConnect +** +** Description This function initiate a cancel connecttion to a ATT server. +** +** Parameters gatt_if: client interface. If 0 used as unconditionally disconnect, +** typically used for direct connection cancellation. +** bd_addr: peer device address. +** is_direct: is a direct conenection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATT_CancelConnect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct); + +/******************************************************************************* +** +** Function GATT_Disconnect +** +** Description This function disconnect a logic channel. +** +** Parameters conn_id: connection identifier. +** +** Returns GATT_SUCCESS if disconnected. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS GATT_Disconnect (UINT16 conn_id); + + + +/******************************************************************************* +** +** Function GATT_GetConnectionInfor +** +** Description This function use conn_id to find its associated BD address and applciation +** interface +** +** Parameters conn_id: connection id (input) +** p_gatt_if: applicaiton interface (output) +** bd_addr: peer device address. (output) +** +** Returns TRUE the ligical link information is found for conn_id +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, BD_ADDR bd_addr); + + +/******************************************************************************* +** +** Function GATT_GetConnIdIfConnected +** +** Description This function find the conn_id if the logical link for BD address +** and applciation interface is connected +** +** Parameters gatt_if: applicaiton interface (input) +** bd_addr: peer device address. (input) +** p_conn_id: connection id (output) +** +** Returns TRUE the ligical link is connected +** +*******************************************************************************/ + GATT_API extern BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, UINT16 *p_conn_id); + +#ifdef __cplusplus + +} +#endif + +#endif /* GATT_API_H */ diff --git a/stack/include/gattdefs.h b/stack/include/gattdefs.h new file mode 100644 index 0000000..6eac0f4 --- /dev/null +++ b/stack/include/gattdefs.h @@ -0,0 +1,109 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used ATT definitions + * + ******************************************************************************/ + +#ifndef _GATTDEFS_H +#define _GATTDEFS_H + +#define GATT_ILLEGAL_UUID 0 + +/* GATT attribute types +*/ +#define GATT_UUID_PRI_SERVICE 0x2800 +#define GATT_UUID_SEC_SERVICE 0x2801 +#define GATT_UUID_INCLUDE_SERVICE 0x2802 +#define GATT_UUID_CHAR_DECLARE 0x2803 /* Characteristic Declaration*/ + +#define GATT_UUID_CHAR_EXT_PROP 0x2900 /* Characteristic Extended Properties */ +#define GATT_UUID_CHAR_DESCRIPTION 0x2901 /* Characteristic User Description*/ +#define GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ +#define GATT_UUID_CHAR_SRVR_CONFIG 0x2903 /* Server Characteristic Configuration */ +#define GATT_UUID_CHAR_PRESENT_FORMAT 0x2904 /* Characteristic Presentation Format*/ +#define GATT_UUID_CHAR_AGG_FORMAT 0x2905 /* Characteristic Aggregate Format*/ +#define GATT_UUID_CHAR_VALID_RANGE 0x2906 /* Characteristic Valid Range */ +#define GATT_UUID_EXT_RPT_REF_DESCR 0x2907 +#define GATT_UUID_RPT_REF_DESCR 0x2908 + + +/* GAP Profile Attributes +*/ +#define GATT_UUID_GAP_DEVICE_NAME 0x2A00 +#define GATT_UUID_GAP_ICON 0x2A01 +#define GATT_UUID_GAP_PRIVACY_FLAG 0x2A02 +#define GATT_UUID_GAP_RECONN_ADDR 0x2A03 +#define GATT_UUID_GAP_PREF_CONN_PARAM 0x2A04 + +/* Attribute Profile Attribute UUID */ +#define GATT_UUID_GATT_SRV_CHGD 0x2A05 +/* Attribute Protocol Test */ + +/* Link Loss Service */ +#define GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ + +/* Time Profile */ +/* Current Time Service */ +#define GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ + +/* NwA Profile */ +#define GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ + +/* phone alert */ +#define GATT_UUID_ALERT_STATUS 0x2A40 /* alert status */ +#define GATT_UUID_RINGER_CP 0x2A42 /* ringer control point */ +#define GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ + +/* Glucose Service */ +#define GATT_UUID_GM_MEASUREMENT 0x2A18 +#define GATT_UUID_GM_CONTEXT 0x2A34 +#define GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define GATT_UUID_GM_FEATURE 0x2A51 + +/* device infor characteristic */ +#define GATT_UUID_SYSTEM_ID 0x2A23 +#define GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define GATT_UUID_FW_VERSION_STR 0x2A26 +#define GATT_UUID_HW_VERSION_STR 0x2A27 +#define GATT_UUID_SW_VERSION_STR 0x2A28 +#define GATT_UUID_MANU_NAME 0x2A29 +#define GATT_UUID_IEEE_DATA 0x2A2A +#define GATT_UUID_PNP_ID 0x2A50 + +/* HID characteristics */ +#define GATT_UUID_HID_INFORMATION 0x2A4A +#define GATT_UUID_HID_REPORT_MAP 0x2A4B +#define GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define GATT_UUID_HID_REPORT 0x2A4D +#define GATT_UUID_HID_PROTO_MODE 0x2A4E +#define GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + +/* Battery Service char */ +#define GATT_UUID_BATTERY_LEVEL 0x2A19 + +#endif diff --git a/stack/include/goep_fs.h b/stack/include/goep_fs.h new file mode 100644 index 0000000..c5d054e --- /dev/null +++ b/stack/include/goep_fs.h @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * Copyright (C) 2000-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. + * + ******************************************************************************/ +#ifndef GOEP_FS_H +#define GOEP_FS_H + +#include "bt_target.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Flags passed to the open function (tGOEP_OPEN_CBACK) +** Values are OR'd together. (First 3 are +** mutually exclusive. +*/ +#define GOEP_O_RDONLY 0x0000 +#define GOEP_O_WRONLY 0x0001 +#define GOEP_O_RDWR 0x0002 + +#define GOEP_O_CREAT 0x0100 +#define GOEP_O_EXCL 0x0200 +#define GOEP_O_TRUNC 0x1000 + + +#define GOEP_LEN_UNKNOWN 0xFFFFFFFF +#define GOEP_INVALID_FD (-1) + +/* Values passed to the access function (tGOEP_ACCESS_CBACK) +*/ +#define GOEP_ACC_EXIST 0x0 +#define GOEP_ACC_READ 0x4 +#define GOEP_ACC_RDWR 0x6 + +/* Constants used in directory listing structure */ +#define GOEP_A_RDONLY 0x1 +#define GOEP_A_DIR 0x2 /* Entry is a sub directory */ + +#define GOEP_CTIME_LEN 17 /* Creation time "yyyymmddTHHMMSSZ" */ + +/***************************************************************************** +** Seek Constants +*****************************************************************************/ +/* Origin for the seek function (tGOEP_SEEK_CBACK) */ +#define GOEP_SEEK_SET 0 +#define GOEP_SEEK_CUR 1 +#define GOEP_SEEK_END 2 + + + +/***************************************************************************** +** Typedefs +*****************************************************************************/ +typedef INT32 tGOEP_FD; + +enum +{ + GOEP_OK, + GOEP_FAIL, + GOEP_EACCES, + GOEP_ENOTEMPTY, + GOEP_EOF, + GOEP_EODIR, + GOEP_ENOSPACE, + GOEP_EIS_DIR, + GOEP_RESUME, + GOEP_NONE +}; +typedef UINT16 tGOEP_STATUS; + +/* Structure passed in Directory Entry Callback to be filled in */ +typedef struct +{ + UINT32 refdata; /* holder for OS specific data used to get next entry */ + UINT32 filesize; + char crtime[GOEP_CTIME_LEN]; /* "yyyymmddTHHMMSSZ", or "" if none */ + char *p_name; /* Contains the addr of memory to copy name into */ + UINT8 mode; /* GOEP_A_RDONLY and/or GOEP_A_DIR */ +} tGOEP_DIRENTRY; + + +/***************************************************************************** +** Typedefs for messages from response functions +*****************************************************************************/ +typedef struct +{ + BT_HDR hdr; + tGOEP_FD fd; + tGOEP_STATUS status; + UINT32 file_size; +} tGOEP_OPEN_RSP; + +typedef struct +{ + BT_HDR hdr; + tGOEP_FD fd; + tGOEP_STATUS status; + UINT16 bytes_read; +} tGOEP_READ_RSP; + +typedef struct +{ + BT_HDR hdr; + tGOEP_FD fd; + tGOEP_STATUS status; +} tGOEP_WRITE_RSP; + +typedef struct +{ + BT_HDR hdr; + tGOEP_STATUS status; +} tGOEP_DIRENTRY_RSP; + +/***************************************************************************** +** Object Store Interface +*****************************************************************************/ +/******************************************************************************* +** +** Callback Function: tGOEP_OPEN_CBACK +** +** Description This function is executed by OBX profiles to open +** a file for reading or writing. +** +** Parameters p_path - Fully qualified path and file name. +** flags - permissions and mode (see constants above) +** size - size of file to put (0 if unavailable or not applicable) +** event_id - code that must be passed to the call-in function. +** +** Returns void +** +** Note: Upon completion of the request, a file descriptor (tGOEP_FD), +** file size (UINT32), and an status code (tGOEP_STATUS) +** are returned in GOEP_OpenRsp(). +** +*******************************************************************************/ +typedef void (tGOEP_OPEN_CBACK) (const UINT8 *p_name, UINT16 flags, UINT32 size, + UINT16 event_id, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_CLOSE_CBACK +** +** Description This function is executed by OBX profiles when the file descriptor +** is no longer in use. +** +** Returns void +** +*******************************************************************************/ +typedef void (tGOEP_CLOSE_CBACK) (tGOEP_FD fd, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_READ_CBACK +** +** Description This function is executed by OBX profiles to read in data from the +** previously opened file. +** +** Returns void +** +** Note: Upon completion of the request, GOEP_ReadRsp() is +** called with the buffer of data, along with the number +** of bytes read into the buffer, and a status. The +** call-in function should only be called when ALL requested +** bytes have been read, the end of file has been detected, +** or an error has occurred. +** +*******************************************************************************/ +typedef void (tGOEP_READ_CBACK) (tGOEP_FD fd, void *p_data, INT16 size, + UINT16 event_id, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_WRITE_CBACK +** +** Description This function is executed by OBX profiles to write the data to the +** previously opened file. +** +** Returns void +** +** Note: Upon completion of the request, GOEP_WriteRsp() is +** called with the file descriptor and the status. The +** call-in function should only be called when ALL requested +** bytes have been written, or an error has been detected, +** +*******************************************************************************/ +typedef void (tGOEP_WRITE_CBACK) (tGOEP_FD fd, const void *p_data, INT16 size, + UINT16 event_id, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_SEEK_CBACK +** +** Description This function is executed by OBX profiles to Move a file pointer +** of a previously opened file to the specified location for the +** next read or write operation. +** +** Returns void +** +*******************************************************************************/ +typedef void (tGOEP_SEEK_CBACK) (tGOEP_FD fd, INT32 offset, INT16 origin, UINT8 app_id); + + +/******************************************************************************* +** +** Callback Function: tGOEP_DIRENTRY_CBACK +** +** Description This function is called to retrieve a directory entry for the +** specified path. The first/next directory should be filled +** into the location specified by p_entry. +** +** Parameters p_path - directory to search (Fully qualified path) +** first_item - TRUE if first search, FALSE if next search +** (p_cur contains previous) +** p_entry (input/output) - Points to last entry data (valid when +** first_item is FALSE) +** event_id - event that must be passed into the call-in function. +** +** Returns void +** +** Note: Upon completion of the request, GOEP_DirentryRsp() is +** filled in entry and the status. +** GOEP_OK is returned when p_entry is valid, +** GOEP_EODIR is returned when no more entries [finished] +** GOEP_FAIL is returned if an error occurred +** +*******************************************************************************/ +typedef void (tGOEP_DIRENTRY_CBACK) (const char *p_path, BOOLEAN first_item, + tGOEP_DIRENTRY *p_entry, UINT16 event_id, + UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_ACCESS_CBACK +** +** Description This function is called to check the existence of a file or +** directory. +** +** Returns (tGOEP_STATUS) status of the call. +** [GOEP_OK if it exists] +** [GOEP_EACCES if permissions are wrong] +** [GOEP_FAIL if it does not exist] +** +*******************************************************************************/ +typedef tGOEP_STATUS (tGOEP_ACCESS_CBACK) (const char *p_path, UINT16 mode, + BOOLEAN *p_is_dir, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_MKDIR_CBACK +** +** Description This function is called to create a directory with +** the pathname given by path. The pathname is a null terminated +** string. All components of the path must already exist. +** +** Parameters p_path - (input) name of directory to create (fully qualified path). +** +** Returns (tGOEP_STATUS) status of the call. +** [GOEP_OK if successful] +** [GOEP_FAIL if unsuccessful] +** +*******************************************************************************/ +typedef tGOEP_STATUS (tGOEP_MKDIR_CBACK) (const char *p_path, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_RMDIR_CBACK +** +** Description This function is called to remove a directory whose +** name is given by path. The directory must be empty. +** +** Parameters p_path - (input) name of directory to remove (fully qualified path). +** +** Returns (tGOEP_STATUS) status of the call. +** [GOEP_OK if successful] +** [GOEP_EACCES if read-only] +** [GOEP_ENOTEMPTY if directory is not empty] +** [GOEP_FAIL otherwise] +** +*******************************************************************************/ +typedef tGOEP_STATUS (tGOEP_RMDIR_CBACK) (const char *p_path, UINT8 app_id); + +/******************************************************************************* +** +** Callback Function: tGOEP_UNLINK_CBACK +** +** Description This function is called to remove a directory whose +** name is given by path. The directory must be empty. +** +** Parameters p_path - (input) name of file to remove (fully qualified path). +** +** Returns (tGOEP_STATUS) status of the call. +** [GOEP_OK if successful] +** [GOEP_EACCES if read-only] +** [GOEP_FAIL otherwise] +** +*******************************************************************************/ +typedef tGOEP_STATUS (tGOEP_UNLINK_CBACK) (const char *p_path, UINT8 app_id); + + +/***************************************************************************** +** Prototypes +*****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************************************************** +** +** Function: GOEP_OpenRsp +** +** Purpose: Report the status of tGOEP_OPEN_CBACK callback function. +** +** Parameters: fd - File handle. +** status - Status of the operation. +** file_size - total number of bytes in this file. +** event_id - event id as given in the tGOEP_OPEN_CBACK function. +** +** Returns: void +** +*****************************************************************************/ +GOEP_API extern void GOEP_OpenRsp (tGOEP_FD fd, tGOEP_STATUS status, + UINT32 file_size, UINT16 event_id); + +/***************************************************************************** +** +** Function: GOEP_ReadRsp +** +** Purpose: Report the status of tGOEP_READ_CBACK callback function. +** +** Parameters: fd - File handle. +** status - Status of the operation. +** bytes_read - total number of bytes read from the file. +** event_id - event id as given in the tGOEP_READ_CBACK function. +** +** Returns: void +** +*****************************************************************************/ +GOEP_API extern void GOEP_ReadRsp (tGOEP_FD fd, tGOEP_STATUS status, + UINT16 bytes_read, UINT16 event_id); + +/***************************************************************************** +** +** Function: GOEP_WriteRsp +** +** Purpose: Report the status of tGOEP_WRITE_CBACK callback function. +** +** Parameters: fd - File handle. +** status - Status of the operation. +** event_id - event id as given in the tGOEP_WRITE_CBACK function. +** +** Returns: void +** +*****************************************************************************/ +GOEP_API extern void GOEP_WriteRsp (tGOEP_FD fd, tGOEP_STATUS status, UINT16 event_id); + +/******************************************************************************* +** +** Function GOEP_DirentryRsp +** +** Description This function is called in response to the +** tGOEP_DIRENTRY_CBACK function with a filled in directory listing +** entry. +** +** Parameters status - GOEP_OK if p_entry points to a valid entry. +** GOEP_EODIR if no more entries (p_entry is ignored). +** GOEP_FAIL if any errors have occurred. +** event_id - event id as given in the tGOEP_DIRENTRY_CBACK function. +** +** Returns void +** +*******************************************************************************/ +GOEP_API extern void GOEP_DirentryRsp(tGOEP_STATUS status, UINT16 event_id); + +#ifdef __cplusplus +} +#endif + +#endif /* GOEP_FS_H */ diff --git a/stack/include/hcidefs.h b/stack/include/hcidefs.h new file mode 100644 index 0000000..1a0b3e8 --- /dev/null +++ b/stack/include/hcidefs.h @@ -0,0 +1,2233 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef HCIDEFS_H +#define HCIDEFS_H + +#ifdef BRCM_VS +#include "brcm_vs_include.h" +#endif + +#define HCI_PROTO_VERSION 0x01 /* Version for BT spec 1.1 */ +#define HCI_PROTO_VERSION_1_2 0x02 /* Version for BT spec 1.2 */ +#define HCI_PROTO_VERSION_2_0 0x03 /* Version for BT spec 2.0 */ +#define HCI_PROTO_VERSION_2_1 0x04 /* Version for BT spec 2.1 [Lisbon] */ +#define HCI_PROTO_VERSION_3_0 0x05 /* Version for BT spec 3.0 */ +#define HCI_PROTO_REVISION 0x000C /* Current implementation version */ +/* +** Definitions for HCI groups +*/ +#define HCI_GRP_LINK_CONTROL_CMDS (0x01 << 10) /* 0x0400 */ +#define HCI_GRP_LINK_POLICY_CMDS (0x02 << 10) /* 0x0800 */ +#define HCI_GRP_HOST_CONT_BASEBAND_CMDS (0x03 << 10) /* 0x0C00 */ +#define HCI_GRP_INFORMATIONAL_PARAMS (0x04 << 10) /* 0x1000 */ +#define HCI_GRP_STATUS_PARAMS (0x05 << 10) /* 0x1400 */ +#define HCI_GRP_TESTING_CMDS (0x06 << 10) /* 0x1800 */ + +#define HCI_GRP_VENDOR_SPECIFIC (0x3F << 10) /* 0xFC00 */ + +/* Group occupies high 6 bits of the HCI command rest is opcode itself */ +#define HCI_OGF(p) (UINT8)((0xFC00 & (p)) >> 10) +#define HCI_OCF(p) ( 0x3FF & (p)) + +/* +** Defentions for Link Control Commands +*/ +/* Following opcode is used only in command complete event for flow control */ +#define HCI_COMMAND_NONE 0x0000 + +/* Commands of HCI_GRP_LINK_CONTROL_CMDS group */ +#define HCI_INQUIRY (0x0001 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_INQUIRY_CANCEL (0x0002 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PERIODIC_INQUIRY_MODE (0x0003 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_EXIT_PERIODIC_INQUIRY_MODE (0x0004 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION (0x0005 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT (0x0006 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ADD_SCO_CONNECTION (0x0007 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION_CANCEL (0x0008 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_CONNECTION_REQUEST (0x0009 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_CONNECTION_REQUEST (0x000A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_REPLY (0x000B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_NEG_REPLY (0x000C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_REPLY (0x000D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_NEG_REPLY (0x000E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_PACKET_TYPE (0x000F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_AUTHENTICATION_REQUESTED (0x0011 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SET_CONN_ENCRYPTION (0x0013 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_LINK_KEY (0x0015 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_MASTER_LINK_KEY (0x0017 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST (0x0019 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST_CANCEL (0x001A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_FEATURES (0x001B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_EXT_FEATURES (0x001C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_VERSION_INFO (0x001D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_CLOCK_OFFSET (0x001F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_LMP_HANDLE (0x0020 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SETUP_ESCO_CONNECTION (0x0028 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_ESCO_CONNECTION (0x0029 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_ESCO_CONNECTION (0x002A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAPABILITY_RESPONSE (0x002B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_REQUEST_REPLY (0x002C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_VALUE_NEG_REPLY (0x002D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_REPLY (0x002E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_NEG_REPLY (0x002F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_REPLY (0x0030 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_NEG_REPLY (0x0033 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAP_REQ_NEG_REPLY (0x0034 | HCI_GRP_LINK_CONTROL_CMDS) + +/* AMP HCI */ +#define HCI_CREATE_PHYSICAL_LINK (0x0035 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_PHYSICAL_LINK (0x0036 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT_PHYSICAL_LINK (0x0037 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_LOGICAL_LINK (0x0038 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_LOGICAL_LINK (0x0039 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT_LOGICAL_LINK (0x003A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LOGICAL_LINK_CANCEL (0x003B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_FLOW_SPEC_MODIFY (0x003C | HCI_GRP_LINK_CONTROL_CMDS) + +#define HCI_LINK_CTRL_CMDS_FIRST HCI_INQUIRY +#define HCI_LINK_CTRL_CMDS_LAST HCI_FLOW_SPEC_MODIFY + +/* Commands of HCI_GRP_LINK_POLICY_CMDS */ +#define HCI_HOLD_MODE (0x0001 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_MODE (0x0003 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_SNIFF_MODE (0x0004 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_PARK_MODE (0x0005 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_PARK_MODE (0x0006 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_QOS_SETUP (0x0007 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_ROLE_DISCOVERY (0x0009 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SWITCH_ROLE (0x000B | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_POLICY_SETTINGS (0x000C | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_POLICY_SETTINGS (0x000D | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_DEF_POLICY_SETTINGS (0x000E | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_DEF_POLICY_SETTINGS (0x000F | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_FLOW_SPECIFICATION (0x0010 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_SUB_RATE (0x0011 | HCI_GRP_LINK_POLICY_CMDS) + +#define HCI_LINK_POLICY_CMDS_FIRST HCI_HOLD_MODE +#define HCI_LINK_POLICY_CMDS_LAST HCI_SNIFF_SUB_RATE + + +/* Commands of HCI_GRP_HOST_CONT_BASEBAND_CMDS */ +#define HCI_SET_EVENT_MASK (0x0001 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_RESET (0x0003 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EVENT_FILTER (0x0005 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_FLUSH (0x0008 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PIN_TYPE (0x0009 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PIN_TYPE (0x000A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CREATE_NEW_UNIT_KEY (0x000B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_STORED_LINK_KEY (0x000D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_STORED_LINK_KEY (0x0011 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_DELETE_STORED_LINK_KEY (0x0012 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CHANGE_LOCAL_NAME (0x0013 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_NAME (0x0014 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CONN_ACCEPT_TOUT (0x0015 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CONN_ACCEPT_TOUT (0x0016 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGE_TOUT (0x0017 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGE_TOUT (0x0018 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCAN_ENABLE (0x0019 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCAN_ENABLE (0x001A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_CFG (0x001B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_CFG (0x001C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRYSCAN_CFG (0x001D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRYSCAN_CFG (0x001E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTHENTICATION_ENABLE (0x001F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTHENTICATION_ENABLE (0x0020 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_ENCRYPTION_MODE (0x0021 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_ENCRYPTION_MODE (0x0022 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CLASS_OF_DEVICE (0x0023 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CLASS_OF_DEVICE (0x0024 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_VOICE_SETTINGS (0x0025 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_VOICE_SETTINGS (0x0026 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTO_FLUSH_TOUT (0x0027 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTO_FLUSH_TOUT (0x0028 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_BCAST_REXMITS (0x0029 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_NUM_BCAST_REXMITS (0x002A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_HOLD_MODE_ACTIVITY (0x002B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_HOLD_MODE_ACTIVITY (0x002C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_TRANSMIT_POWER_LEVEL (0x002D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCO_FLOW_CTRL_ENABLE (0x002E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCO_FLOW_CTRL_ENABLE (0x002F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_HC_TO_HOST_FLOW_CTRL (0x0031 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_BUFFER_SIZE (0x0033 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_NUM_PACKETS_DONE (0x0035 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LINK_SUPER_TOUT (0x0036 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LINK_SUPER_TOUT (0x0037 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_SUPPORTED_IAC (0x0038 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CURRENT_IAC_LAP (0x0039 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CURRENT_IAC_LAP (0x003A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_PERIOD_MODE (0x003B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_PERIOD_MODE (0x003C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_MODE (0x003D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_MODE (0x003E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_AFH_CHANNELS (0x003F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_READ_INQSCAN_TYPE (0x0042 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQSCAN_TYPE (0x0043 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRY_MODE (0x0044 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRY_MODE (0x0045 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_TYPE (0x0046 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_TYPE (0x0047 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AFH_ASSESSMENT_MODE (0x0048 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AFH_ASSESSMENT_MODE (0x0049 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_EXT_INQ_RESPONSE (0x0051 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_EXT_INQ_RESPONSE (0x0052 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_REFRESH_ENCRYPTION_KEY (0x0053 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SIMPLE_PAIRING_MODE (0x0055 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SIMPLE_PAIRING_MODE (0x0056 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_OOB_DATA (0x0057 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQ_TX_POWER_LEVEL (0x0058 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQ_TX_POWER_LEVEL (0x0059 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_ERRONEOUS_DATA_RPT (0x005A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_ERRONEOUS_DATA_RPT (0x005B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_ENHANCED_FLUSH (0x005F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SEND_KEYPRESS_NOTIF (0x0060 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + + +/* AMP HCI */ +#define HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT (0x0061 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT (0x0062 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EVENT_MASK_PAGE_2 (0x0063 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCATION_DATA (0x0064 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LOCATION_DATA (0x0065 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_FLOW_CONTROL_MODE (0x0066 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_FLOW_CONTROL_MODE (0x0067 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_BE_FLUSH_TOUT (0x0069 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_BE_FLUSH_TOUT (0x006A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SHORT_RANGE_MODE (0x006B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) /* 802.11 only */ + +#define HCI_CONT_BASEBAND_CMDS_FIRST HCI_SET_EVENT_MASK +#define HCI_CONT_BASEBAND_CMDS_LAST HCI_SHORT_RANGE_MODE + + +/* Commands of HCI_GRP_INFORMATIONAL_PARAMS group */ +#define HCI_READ_LOCAL_VERSION_INFO (0x0001 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_SUPPORTED_CMDS (0x0002 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_FEATURES (0x0003 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_EXT_FEATURES (0x0004 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BUFFER_SIZE (0x0005 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_COUNTRY_CODE (0x0007 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BD_ADDR (0x0009 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_DATA_BLOCK_SIZE (0x000A | HCI_GRP_INFORMATIONAL_PARAMS) + +#define HCI_INFORMATIONAL_CMDS_FIRST HCI_READ_LOCAL_VERSION_INFO +#define HCI_INFORMATIONAL_CMDS_LAST HCI_READ_BD_ADDR + + +/* Commands of HCI_GRP_STATUS_PARAMS group */ +#define HCI_READ_FAILED_CONTACT_COUNT (0x0001 | HCI_GRP_STATUS_PARAMS) +#define HCI_RESET_FAILED_CONTACT_COUNT (0x0002 | HCI_GRP_STATUS_PARAMS) +#define HCI_GET_LINK_QUALITY (0x0003 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_RSSI (0x0005 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_AFH_CH_MAP (0x0006 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_CLOCK (0x0007 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_ENCR_KEY_SIZE (0x0008 | HCI_GRP_STATUS_PARAMS) + +/* AMP HCI */ +#define HCI_READ_LOCAL_AMP_INFO (0x0009 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_LOCAL_AMP_ASSOC (0x000A | HCI_GRP_STATUS_PARAMS) +#define HCI_WRITE_REMOTE_AMP_ASSOC (0x000B | HCI_GRP_STATUS_PARAMS) + +#define HCI_STATUS_PARAMS_CMDS_FIRST HCI_READ_FAILED_CONTACT_COUNT +#define HCI_STATUS_PARAMS_CMDS_LAST HCI_WRITE_REMOTE_AMP_ASSOC + +/* Commands of HCI_GRP_TESTING_CMDS group */ +#define HCI_READ_LOOPBACK_MODE (0x0001 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_LOOPBACK_MODE (0x0002 | HCI_GRP_TESTING_CMDS) +#define HCI_ENABLE_DEV_UNDER_TEST_MODE (0x0003 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_SIMP_PAIR_DEBUG_MODE (0x0004 | HCI_GRP_TESTING_CMDS) + +/* AMP HCI */ +#define HCI_ENABLE_AMP_RCVR_REPORTS (0x0007 | HCI_GRP_TESTING_CMDS) +#define HCI_AMP_TEST_END (0x0008 | HCI_GRP_TESTING_CMDS) +#define HCI_AMP_TEST (0x0009 | HCI_GRP_TESTING_CMDS) + +#define HCI_TESTING_CMDS_FIRST HCI_READ_LOOPBACK_MODE +#define HCI_TESTING_CMDS_LAST HCI_AMP_TEST + +#define HCI_VENDOR_CMDS_FIRST 0x0001 +#define HCI_VENDOR_CMDS_LAST 0xFFFF +#define HCI_VSC_MULTI_AV_HANDLE 0x0AAA +#define HCI_VSC_BURST_MODE_HANDLE 0x0BBB + +/* BLE HCI */ +#define HCI_GRP_BLE_CMDS (0x08 << 10) +/* Commands of BLE Controller setup and configuration */ +#define HCI_BLE_SET_EVENT_MASK (0x0001 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_BUFFER_SIZE (0x0002 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_LOCAL_SPT_FEAT (0x0003 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_LOCAL_SPT_FEAT (0x0004 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_RANDOM_ADDR (0x0005 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_PARAMS (0x0006 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_ADV_CHNL_TX_POWER (0x0007 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_DATA (0x0008 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_RSP_DATA (0x0009 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_ADV_ENABLE (0x000A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_PARAMS (0x000B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_WRITE_SCAN_ENABLE (0x000C | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CREATE_LL_CONN (0x000D | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CREATE_CONN_CANCEL (0x000E | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_WHITE_LIST_SIZE (0x000F | HCI_GRP_BLE_CMDS) +#define HCI_BLE_CLEAR_WHITE_LIST (0x0010 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ADD_WHITE_LIST (0x0011 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_REMOVE_WHITE_LIST (0x0012 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_UPD_LL_CONN_PARAMS (0x0013 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_SET_HOST_CHNL_CLASS (0x0014 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_CHNL_MAP (0x0015 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_REMOTE_FEAT (0x0016 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_ENCRYPT (0x0017 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_RAND (0x0018 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_START_ENC (0x0019 | HCI_GRP_BLE_CMDS) +#define HCI_BLE_LTK_REQ_REPLY (0x001A | HCI_GRP_BLE_CMDS) +#define HCI_BLE_LTK_REQ_NEG_REPLY (0x001B | HCI_GRP_BLE_CMDS) +#define HCI_BLE_READ_SUPPORTED_STATES (0x001C | HCI_GRP_BLE_CMDS) + +#define HCI_BLE_RESET (0x0020 | HCI_GRP_BLE_CMDS) + +/* LE supported states definition */ +#define HCI_LE_ADV_STATE 0x00000001 +#define HCI_LE_SCAN_STATE 0x00000002 +#define HCI_LE_INIT_STATE 0x00000004 +#define HCI_LE_CONN_SL_STATE 0x00000008 +#define HCI_LE_ADV_SCAN_STATE 0x00000010 +#define HCI_LE_ADV_INIT_STATE 0x00000020 +#define HCI_LE_ADV_MA_STATE 0x00000040 +#define HCI_LE_ADV_SL_STATE 0x00000080 +#define HCI_LE_SCAN_INIT_STATE 0x00000100 +#define HCI_LE_SCAN_MA_STATE 0x00000200 +#define HCI_LE_SCAN_SL_STATE 0x00000400 +#define HCI_LE_INIT_MA_STATE 0x00000800 + +/* +** Definitions for HCI Events +*/ +#define HCI_INQUIRY_COMP_EVT 0x01 +#define HCI_INQUIRY_RESULT_EVT 0x02 +#define HCI_CONNECTION_COMP_EVT 0x03 +#define HCI_CONNECTION_REQUEST_EVT 0x04 +#define HCI_DISCONNECTION_COMP_EVT 0x05 +#define HCI_AUTHENTICATION_COMP_EVT 0x06 +#define HCI_RMT_NAME_REQUEST_COMP_EVT 0x07 +#define HCI_ENCRYPTION_CHANGE_EVT 0x08 +#define HCI_CHANGE_CONN_LINK_KEY_EVT 0x09 +#define HCI_MASTER_LINK_KEY_COMP_EVT 0x0A +#define HCI_READ_RMT_FEATURES_COMP_EVT 0x0B +#define HCI_READ_RMT_VERSION_COMP_EVT 0x0C +#define HCI_QOS_SETUP_COMP_EVT 0x0D +#define HCI_COMMAND_COMPLETE_EVT 0x0E +#define HCI_COMMAND_STATUS_EVT 0x0F +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_FLUSH_OCCURED_EVT 0x11 +#define HCI_ROLE_CHANGE_EVT 0x12 +#define HCI_NUM_COMPL_DATA_PKTS_EVT 0x13 +#define HCI_MODE_CHANGE_EVT 0x14 +#define HCI_RETURN_LINK_KEYS_EVT 0x15 +#define HCI_PIN_CODE_REQUEST_EVT 0x16 +#define HCI_LINK_KEY_REQUEST_EVT 0x17 +#define HCI_LINK_KEY_NOTIFICATION_EVT 0x18 +#define HCI_LOOPBACK_COMMAND_EVT 0x19 +#define HCI_DATA_BUF_OVERFLOW_EVT 0x1A +#define HCI_MAX_SLOTS_CHANGED_EVT 0x1B +#define HCI_READ_CLOCK_OFF_COMP_EVT 0x1C +#define HCI_CONN_PKT_TYPE_CHANGE_EVT 0x1D +#define HCI_QOS_VIOLATION_EVT 0x1E +#define HCI_PAGE_SCAN_MODE_CHANGE_EVT 0x1F +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EVT 0x20 +#define HCI_FLOW_SPECIFICATION_COMP_EVT 0x21 +#define HCI_INQUIRY_RSSI_RESULT_EVT 0x22 +#define HCI_READ_RMT_EXT_FEATURES_COMP_EVT 0x23 +#define HCI_ESCO_CONNECTION_COMP_EVT 0x2C +#define HCI_ESCO_CONNECTION_CHANGED_EVT 0x2D +#define HCI_SNIFF_SUB_RATE_EVT 0x2E +#define HCI_EXTENDED_INQUIRY_RESULT_EVT 0x2F +#define HCI_ENCRYPTION_KEY_REFRESH_COMP_EVT 0x30 +#define HCI_IO_CAPABILITY_REQUEST_EVT 0x31 +#define HCI_IO_CAPABILITY_RESPONSE_EVT 0x32 +#define HCI_USER_CONFIRMATION_REQUEST_EVT 0x33 +#define HCI_USER_PASSKEY_REQUEST_EVT 0x34 +#define HCI_REMOTE_OOB_DATA_REQUEST_EVT 0x35 +#define HCI_SIMPLE_PAIRING_COMPLETE_EVT 0x36 +#define HCI_LINK_SUPER_TOUT_CHANGED_EVT 0x38 +#define HCI_ENHANCED_FLUSH_COMPLETE_EVT 0x39 +#define HCI_USER_PASSKEY_NOTIFY_EVT 0x3B +#define HCI_KEYPRESS_NOTIFY_EVT 0x3C +#define HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT 0x3D + +/*#define HCI_GENERIC_AMP_LINK_KEY_NOTIF_EVT 0x3E Removed from spec */ +#define HCI_PHYSICAL_LINK_COMP_EVT 0x40 +#define HCI_CHANNEL_SELECTED_EVT 0x41 +#define HCI_DISC_PHYSICAL_LINK_COMP_EVT 0x42 +#define HCI_PHY_LINK_LOSS_EARLY_WARNING_EVT 0x43 +#define HCI_PHY_LINK_RECOVERY_EVT 0x44 +#define HCI_LOGICAL_LINK_COMP_EVT 0x45 +#define HCI_DISC_LOGICAL_LINK_COMP_EVT 0x46 +#define HCI_FLOW_SPEC_MODIFY_COMP_EVT 0x47 +#define HCI_NUM_COMPL_DATA_BLOCKS_EVT 0x48 +#define HCI_SHORT_RANGE_MODE_COMPLETE_EVT 0x4C +#define HCI_AMP_STATUS_CHANGE_EVT 0x4D + +/* ULP HCI Event */ +#define HCI_BLE_EVENT 0x03E +/* ULP Event sub code */ +#define HCI_BLE_CONN_COMPLETE_EVT 0x01 +#define HCI_BLE_ADV_PKT_RPT_EVT 0x02 +#define HCI_BLE_LL_CONN_PARAM_UPD_EVT 0x03 +#define HCI_BLE_READ_REMOTE_FEAT_CMPL_EVT 0x04 +#define HCI_BLE_LTK_REQ_EVT 0x05 + +#define HCI_EVENT_RSP_FIRST HCI_INQUIRY_COMP_EVT +#define HCI_EVENT_RSP_LAST HCI_AMP_STATUS_CHANGE_EVT + +#define HCI_VENDOR_SPECIFIC_EVT 0xFF /* Vendor specific events */ +#define HCI_NAP_TRACE_EVT 0xFF /* was define 0xFE, 0xFD, change to 0xFF + because conflict w/ TCI_EVT and per + specification compliant */ + + + +/* +** Defentions for HCI Error Codes that are past in the events +*/ +#define HCI_SUCCESS 0x00 +#define HCI_PENDING 0x00 +#define HCI_ERR_ILLEGAL_COMMAND 0x01 +#define HCI_ERR_NO_CONNECTION 0x02 +#define HCI_ERR_HW_FAILURE 0x03 +#define HCI_ERR_PAGE_TIMEOUT 0x04 +#define HCI_ERR_AUTH_FAILURE 0x05 +#define HCI_ERR_KEY_MISSING 0x06 +#define HCI_ERR_MEMORY_FULL 0x07 +#define HCI_ERR_CONNECTION_TOUT 0x08 +#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 +#define HCI_ERR_MAX_NUM_OF_SCOS 0x0A +#define HCI_ERR_CONNECTION_EXISTS 0x0B +#define HCI_ERR_COMMAND_DISALLOWED 0x0C +#define HCI_ERR_HOST_REJECT_RESOURCES 0x0D +#define HCI_ERR_HOST_REJECT_SECURITY 0x0E +#define HCI_ERR_HOST_REJECT_DEVICE 0x0F +#define HCI_ERR_HOST_TIMEOUT 0x10 +#define HCI_ERR_UNSUPPORTED_VALUE 0x11 +#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 +#define HCI_ERR_PEER_USER 0x13 +#define HCI_ERR_PEER_LOW_RESOURCES 0x14 +#define HCI_ERR_PEER_POWER_OFF 0x15 +#define HCI_ERR_CONN_CAUSE_LOCAL_HOST 0x16 +#define HCI_ERR_REPEATED_ATTEMPTS 0x17 +#define HCI_ERR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERR_UNKNOWN_LMP_PDU 0x19 +#define HCI_ERR_UNSUPPORTED_REM_FEATURE 0x1A +#define HCI_ERR_SCO_OFFSET_REJECTED 0x1B +#define HCI_ERR_SCO_INTERVAL_REJECTED 0x1C +#define HCI_ERR_SCO_AIR_MODE 0x1D +#define HCI_ERR_INVALID_LMP_PARAM 0x1E +#define HCI_ERR_UNSPECIFIED 0x1F +#define HCI_ERR_UNSUPPORTED_LMP_FEATURE 0x20 +#define HCI_ERR_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_ERR_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_ERR_LMP_ERR_TRANS_COLLISION 0x23 +#define HCI_ERR_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_ERR_UNIT_KEY_USED 0x26 +#define HCI_ERR_QOS_NOT_SUPPORTED 0x27 +#define HCI_ERR_INSTANT_PASSED 0x28 +#define HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED 0x29 +#define HCI_ERR_DIFF_TRANSACTION_COLLISION 0x2A +#define HCI_ERR_UNDEFINED_0x2B 0x2B +#define HCI_ERR_QOS_UNACCEPTABLE_PARAM 0x2C +#define HCI_ERR_QOS_REJECTED 0x2D +#define HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED 0x2E +#define HCI_ERR_INSUFFCIENT_SECURITY 0x2F +#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 +#define HCI_ERR_UNDEFINED_0x31 0x31 +#define HCI_ERR_ROLE_SWITCH_PENDING 0x32 +#define HCI_ERR_UNDEFINED_0x33 0x33 +#define HCI_ERR_RESERVED_SLOT_VIOLATION 0x34 +#define HCI_ERR_ROLE_SWITCH_FAILED 0x35 +#define HCI_ERR_INQ_RSP_DATA_TOO_LARGE 0x36 +#define HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED 0x37 +#define HCI_ERR_HOST_BUSY_PAIRING 0x38 +#define HCI_ERR_REJ_NO_SUITABLE_CHANNEL 0x39 +#define HCI_ERR_CONTROLLER_BUSY 0x3A +#define HCI_ERR_UNACCEPT_CONN_INTERVAL 0x3B +#define HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT 0x3C +#define HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE 0x3D +#define HCI_ERR_CONN_FAILED_ESTABLISHMENT 0x3E +#define HCI_ERR_MAC_CONNECTION_FAILED 0x3F + +#define HCI_ERR_MAX_ERR 0x40 + +#define HCI_HINT_TO_RECREATE_AMP_PHYS_LINK 0xFF + +/* +** Definitions for HCI enable event +*/ +#define HCI_INQUIRY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000001) +#define HCI_INQUIRY_RESULT_EV(p) (*((UINT32 *)(p)) & 0x00000002) +#define HCI_CONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000004) +#define HCI_CONNECTION_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00000008) +#define HCI_DISCONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000010) +#define HCI_AUTHENTICATION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000020) +#define HCI_RMT_NAME_REQUEST_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000040) +#define HCI_CHANGE_CONN_ENCRPT_ENABLE_EV(p) (*((UINT32 *)(p)) & 0x00000080) +#define HCI_CHANGE_CONN_LINK_KEY_EV(p) (*((UINT32 *)(p)) & 0x00000100) +#define HCI_MASTER_LINK_KEY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000200) +#define HCI_READ_RMT_FEATURES_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000400) +#define HCI_READ_RMT_VERSION_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000800) +#define HCI_QOS_SETUP_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00001000) +#define HCI_COMMAND_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00002000) +#define HCI_COMMAND_STATUS_EV(p) (*((UINT32 *)(p)) & 0x00004000) +#define HCI_HARDWARE_ERROR_EV(p) (*((UINT32 *)(p)) & 0x00008000) +#define HCI_FLASH_OCCURED_EV(p) (*((UINT32 *)(p)) & 0x00010000) +#define HCI_ROLE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00020000) +#define HCI_NUM_COMPLETED_PKTS_EV(p) (*((UINT32 *)(p)) & 0x00040000) +#define HCI_MODE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00080000) +#define HCI_RETURN_LINK_KEYS_EV(p) (*((UINT32 *)(p)) & 0x00100000) +#define HCI_PIN_CODE_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00200000) +#define HCI_LINK_KEY_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00400000) +#define HCI_LINK_KEY_NOTIFICATION_EV(p) (*((UINT32 *)(p)) & 0x00800000) +#define HCI_LOOPBACK_COMMAND_EV(p) (*((UINT32 *)(p)) & 0x01000000) +#define HCI_DATA_BUF_OVERFLOW_EV(p) (*((UINT32 *)(p)) & 0x02000000) +#define HCI_MAX_SLOTS_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x04000000) +#define HCI_READ_CLOCK_OFFSET_COMP_EV(p) (*((UINT32 *)(p)) & 0x08000000) +#define HCI_CONN_PKT_TYPE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x10000000) +#define HCI_QOS_VIOLATION_EV(p) (*((UINT32 *)(p)) & 0x20000000) +#define HCI_PAGE_SCAN_MODE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x40000000) +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EV(p) (*((UINT32 *)(p)) & 0x80000000) + +/* the default event mask for 2.1+EDR (Lisbon) does not include Lisbon events */ +#define HCI_DEFAULT_EVENT_MASK_0 0xFFFFFFFF +#define HCI_DEFAULT_EVENT_MASK_1 0x00001FFF + +/* the event mask for 2.0 + EDR and later (includes Lisbon events) */ +#define HCI_LISBON_EVENT_MASK_0 0xFFFFFFFF +#define HCI_LISBON_EVENT_MASK_1 0x1DBFFFFF +#define HCI_LISBON_EVENT_MASK "\x0D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +#define HCI_LISBON_EVENT_MASK_EXT "\x1D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +#define HCI_DUMO_EVENT_MASK_EXT "\x3D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +/* 0x00001FFF FFFFFFFF Default - no Lisbon events + 0x00000800 00000000 Synchronous Connection Complete Event + 0x00001000 00000000 Synchronous Connection Changed Event + 0x00002000 00000000 Sniff Subrate Event + 0x00004000 00000000 Extended Inquiry Result Event + 0x00008000 00000000 Encryption Key Refresh Complete Event + 0x00010000 00000000 IO Capability Request Event + 0x00020000 00000000 IO Capability Response Event + 0x00040000 00000000 User Confirmation Request Event + 0x00080000 00000000 User Passkey Request Event + 0x00100000 00000000 Remote OOB Data Request Event + 0x00200000 00000000 Simple Pairing Complete Event + 0x00400000 00000000 Generic AMP Link Key Notification Event + 0x00800000 00000000 Link Supervision Timeout Changed Event + 0x01000000 00000000 Enhanced Flush Complete Event + 0x04000000 00000000 User Passkey Notification Event + 0x08000000 00000000 Keypress Notification Event + 0x10000000 00000000 Remote Host Supported Features Notification Event + 0x20000000 00000000 LE Meta Event + */ + + +/* the event mask for AMP controllers */ +#define HCI_AMP_EVENT_MASK_3_0 "\x00\x00\x00\x00\x00\x00\x3F\xFF" + +/* 0x0000000000000000 No events specified (default) + 0x0000000000000001 Physical Link Complete Event + 0x0000000000000002 Channel Selected Event + 0x0000000000000004 Disconnection Physical Link Event + 0x0000000000000008 Physical Link Loss Early Warning Event + 0x0000000000000010 Physical Link Recovery Event + 0x0000000000000020 Logical Link Complete Event + 0x0000000000000040 Disconnection Logical Link Complete Event + 0x0000000000000080 Flow Spec Modify Complete Event + 0x0000000000000100 Number of Completed Data Blocks Event + 0x0000000000000200 AMP Start Test Event + 0x0000000000000400 AMP Test End Event + 0x0000000000000800 AMP Receiver Report Event + 0x0000000000001000 Short Range Mode Change Complete Event + 0x0000000000002000 AMP Status Change Event +*/ + + +/* +** Definitions for packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_PKT_TYPES_MASK_NO_2_DH1 0x0002 +#define HCI_PKT_TYPES_MASK_NO_3_DH1 0x0004 +#define HCI_PKT_TYPES_MASK_DM1 0x0008 +#define HCI_PKT_TYPES_MASK_DH1 0x0010 +#define HCI_PKT_TYPES_MASK_HV1 0x0020 +#define HCI_PKT_TYPES_MASK_HV2 0x0040 +#define HCI_PKT_TYPES_MASK_HV3 0x0080 +#define HCI_PKT_TYPES_MASK_NO_2_DH3 0x0100 +#define HCI_PKT_TYPES_MASK_NO_3_DH3 0x0200 +#define HCI_PKT_TYPES_MASK_DM3 0x0400 +#define HCI_PKT_TYPES_MASK_DH3 0x0800 +#define HCI_PKT_TYPES_MASK_NO_2_DH5 0x1000 +#define HCI_PKT_TYPES_MASK_NO_3_DH5 0x2000 +#define HCI_PKT_TYPES_MASK_DM5 0x4000 +#define HCI_PKT_TYPES_MASK_DH5 0x8000 + +/* Packet type should be one of valid but at least one should be specified */ +#define HCI_VALID_SCO_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + + + + + +/* Packet type should not be invalid and at least one should be specified */ +#define HCI_VALID_ACL_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5 \ + | HCI_PKT_TYPES_MASK_NO_2_DH1 \ + | HCI_PKT_TYPES_MASK_NO_3_DH1 \ + | HCI_PKT_TYPES_MASK_NO_2_DH3 \ + | HCI_PKT_TYPES_MASK_NO_3_DH3 \ + | HCI_PKT_TYPES_MASK_NO_2_DH5 \ + | HCI_PKT_TYPES_MASK_NO_3_DH5 )) == 0)) \ + && (((t) & (HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5)) != 0)) + +/* +** Definitions for eSCO packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_ESCO_PKT_TYPES_MASK_HV1 0x0001 +#define HCI_ESCO_PKT_TYPES_MASK_HV2 0x0002 +#define HCI_ESCO_PKT_TYPES_MASK_HV3 0x0004 +#define HCI_ESCO_PKT_TYPES_MASK_EV3 0x0008 +#define HCI_ESCO_PKT_TYPES_MASK_EV4 0x0010 +#define HCI_ESCO_PKT_TYPES_MASK_EV5 0x0020 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 0x0040 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 0x0080 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 0x0100 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5 0x0200 + +/* Packet type should be one of valid but at least one should be specified for 1.2 */ +#define HCI_VALID_ESCO_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0))/* Packet type should be one of valid but at least one should be specified */ + +#define HCI_VALID_ESCO_SCOPKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + +#define HCI_VALID_SCO_ALL_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0)) + +/* +** Define parameters to allow role switch during create connection +*/ +#define HCI_CR_CONN_NOT_ALLOW_SWITCH 0x00 +#define HCI_CR_CONN_ALLOW_SWITCH 0x01 + +/* +** Hold Mode command destination +*/ +#define HOLD_MODE_DEST_LOCAL_DEVICE 0x00 +#define HOLD_MODE_DEST_RMT_DEVICE 0x01 + +/* +** Definitions for different HCI parameters +*/ +#define HCI_PER_INQ_MIN_MAX_PERIOD 0x0003 +#define HCI_PER_INQ_MAX_MAX_PERIOD 0xFFFF +#define HCI_PER_INQ_MIN_MIN_PERIOD 0x0002 +#define HCI_PER_INQ_MAX_MIN_PERIOD 0xFFFE + +#define HCI_MAX_INQUIRY_LENGTH 0x30 + +#define HCI_MIN_INQ_LAP 0x9E8B00 +#define HCI_MAX_INQ_LAP 0x9E8B3F + +/* HCI role defenitions */ +#define HCI_ROLE_MASTER 0x00 +#define HCI_ROLE_SLAVE 0x01 +#define HCI_ROLE_UNKNOWN 0xff + +/* HCI mode defenitions */ +#define HCI_MODE_ACTIVE 0x00 +#define HCI_MODE_HOLD 0x01 +#define HCI_MODE_SNIFF 0x02 +#define HCI_MODE_PARK 0x03 + +/* HCI Flow Control Mode defenitions */ +#define HCI_PACKET_BASED_FC_MODE 0x00 +#define HCI_BLOCK_BASED_FC_MODE 0x01 + +/* Define Packet types as requested by the Host */ +#define HCI_ACL_PKT_TYPE_NONE 0x0000 +#define HCI_ACL_PKT_TYPE_DM1 0x0008 +#define HCI_ACL_PKT_TYPE_DH1 0x0010 +#define HCI_ACL_PKT_TYPE_AUX1 0x0200 +#define HCI_ACL_PKT_TYPE_DM3 0x0400 +#define HCI_ACL_PKT_TYPE_DH3 0x0800 +#define HCI_ACL_PKT_TYPE_DM5 0x4000 +#define HCI_ACL_PKT_TYPE_DH5 0x8000 + +/* Define key type in the Master Link Key command */ +#define HCI_USE_SEMI_PERMANENT_KEY 0x00 +#define HCI_USE_TEMPORARY_KEY 0x01 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_REP_MODE_R0 0x00 +#define HCI_PAGE_SCAN_REP_MODE_R1 0x01 +#define HCI_PAGE_SCAN_REP_MODE_R2 0x02 + +/* Define limits for page scan repetition modes */ +#define HCI_PAGE_SCAN_R1_LIMIT 0x0800 +#define HCI_PAGE_SCAN_R2_LIMIT 0x1000 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_PER_MODE_P0 0x00 +#define HCI_PAGE_SCAN_PER_MODE_P1 0x01 +#define HCI_PAGE_SCAN_PER_MODE_P2 0x02 + +/* Page scan modes */ +#define HCI_MANDATARY_PAGE_SCAN_MODE 0x00 +#define HCI_OPTIONAL_PAGE_SCAN_MODE1 0x01 +#define HCI_OPTIONAL_PAGE_SCAN_MODE2 0x02 +#define HCI_OPTIONAL_PAGE_SCAN_MODE3 0x03 + +/* Page and inquiry scan types */ +#define HCI_SCAN_TYPE_STANDARD 0x00 +#define HCI_SCAN_TYPE_INTERLACED 0x01 /* 1.2 devices or later */ +#define HCI_DEF_SCAN_TYPE HCI_SCAN_TYPE_STANDARD + +/* Definitions for quality of service service types */ +#define HCI_SERVICE_NO_TRAFFIC 0x00 +#define HCI_SERVICE_BEST_EFFORT 0x01 +#define HCI_SERVICE_GUARANTEED 0x02 + +#define HCI_QOS_LATENCY_DO_NOT_CARE 0xFFFFFFFF +#define HCI_QOS_DELAY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for Flow Specification */ +#define HCI_FLOW_SPEC_LATENCY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for AFH Channel Map */ +#define HCI_AFH_CHANNEL_MAP_LEN 10 + +/* Definitions for Extended Inquiry Response */ +#define HCI_EXT_INQ_RESPONSE_LEN 240 +#define HCI_EIR_FLAGS_TYPE BT_EIR_FLAGS_TYPE +#define HCI_EIR_MORE_16BITS_UUID_TYPE BT_EIR_MORE_16BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_16BITS_UUID_TYPE BT_EIR_COMPLETE_16BITS_UUID_TYPE +#define HCI_EIR_MORE_32BITS_UUID_TYPE BT_EIR_MORE_32BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_32BITS_UUID_TYPE BT_EIR_COMPLETE_32BITS_UUID_TYPE +#define HCI_EIR_MORE_128BITS_UUID_TYPE BT_EIR_MORE_128BITS_UUID_TYPE +#define HCI_EIR_COMPLETE_128BITS_UUID_TYPE BT_EIR_COMPLETE_128BITS_UUID_TYPE +#define HCI_EIR_SHORTENED_LOCAL_NAME_TYPE BT_EIR_SHORTENED_LOCAL_NAME_TYPE +#define HCI_EIR_COMPLETE_LOCAL_NAME_TYPE BT_EIR_COMPLETE_LOCAL_NAME_TYPE +#define HCI_EIR_TX_POWER_LEVEL_TYPE BT_EIR_TX_POWER_LEVEL_TYPE +#define HCI_EIR_MANUFACTURER_SPECIFIC_TYPE BT_EIR_MANUFACTURER_SPECIFIC_TYPE +#define HCI_EIR_OOB_BD_ADDR_TYPE BT_EIR_OOB_BD_ADDR_TYPE +#define HCI_EIR_OOB_COD_TYPE BT_EIR_OOB_COD_TYPE +#define HCI_EIR_OOB_SSP_HASH_C_TYPE BT_EIR_OOB_SSP_HASH_C_TYPE +#define HCI_EIR_OOB_SSP_RAND_R_TYPE BT_EIR_OOB_SSP_RAND_R_TYPE + +/* Definitions for Write Simple Pairing Mode */ +#define HCI_SP_MODE_UNDEFINED 0x00 +#define HCI_SP_MODE_ENABLED 0x01 + +/* Definitions for Write Simple Pairing Debug Mode */ +#define HCI_SPD_MODE_DISABLED 0x00 +#define HCI_SPD_MODE_ENABLED 0x01 + +/* Definitions for IO Capability Response/Command */ +#define HCI_IO_CAP_DISPLAY_ONLY 0x00 +#define HCI_IO_CAP_DISPLAY_YESNO 0x01 +#define HCI_IO_CAP_KEYBOARD_ONLY 0x02 +#define HCI_IO_CAP_NO_IO 0x03 + +#define HCI_OOB_AUTH_DATA_NOT_PRESENT 0x00 +#define HCI_OOB_REM_AUTH_DATA_PRESENT 0x01 + +#define HCI_MITM_PROTECT_NOT_REQUIRED 0x00 +#define HCI_MITM_PROTECT_REQUIRED 0x01 + + +/* Policy settings status */ +#define HCI_DISABLE_ALL_LM_MODES 0x0000 +#define HCI_ENABLE_MASTER_SLAVE_SWITCH 0x0001 +#define HCI_ENABLE_HOLD_MODE 0x0002 +#define HCI_ENABLE_SNIFF_MODE 0x0004 +#define HCI_ENABLE_PARK_MODE 0x0008 + +/* By default allow switch, because host can not allow that */ +/* that until he created the connection */ +#define HCI_DEFAULT_POLICY_SETTINGS HCI_DISABLE_ALL_LM_MODES + +/* Filters that are sent in set filter command */ +#define HCI_FILTER_TYPE_CLEAR_ALL 0x00 +#define HCI_FILTER_INQUIRY_RESULT 0x01 +#define HCI_FILTER_CONNECTION_SETUP 0x02 + +#define HCI_FILTER_COND_NEW_DEVICE 0x00 +#define HCI_FILTER_COND_DEVICE_CLASS 0x01 +#define HCI_FILTER_COND_BD_ADDR 0x02 + +#define HCI_DO_NOT_AUTO_ACCEPT_CONNECT 1 +#define HCI_DO_AUTO_ACCEPT_CONNECT 2 /* role switch disabled */ +#define HCI_DO_AUTO_ACCEPT_CONNECT_RS 3 /* role switch enabled (1.1 errata 1115) */ + +/* Auto accept flags */ +#define HCI_AUTO_ACCEPT_OFF 0x00 +#define HCI_AUTO_ACCEPT_ACL_CONNECTIONS 0x01 +#define HCI_AUTO_ACCEPT_SCO_CONNECTIONS 0x02 + +/* PIN type */ +#define HCI_PIN_TYPE_VARIABLE 0 +#define HCI_PIN_TYPE_FIXED 1 + +/* Loopback Modes */ +#define HCI_LOOPBACK_MODE_DISABLED 0 +#define HCI_LOOPBACK_MODE_LOCAL 1 +#define HCI_LOOPBACK_MODE_REMOTE 2 + +#define SLOTS_PER_10MS 16 /* 0.625 ms slots in a 10 ms tick */ + +/* Maximum connection accept timeout in 0.625msec */ +#define HCI_MAX_CONN_ACCEPT_TOUT 0xB540 /* 29 sec */ +#define HCI_DEF_CONN_ACCEPT_TOUT 0x1F40 /* 5 sec */ + +/* Page timeout is used in LC only and LC is counting down slots not using OS */ +#define HCI_DEFAULT_PAGE_TOUT 0x2000 /* 5.12 sec (in slots) */ + +/* Scan enable flags */ +#define HCI_NO_SCAN_ENABLED 0x00 +#define HCI_INQUIRY_SCAN_ENABLED 0x01 +#define HCI_PAGE_SCAN_ENABLED 0x02 + +/* Pagescan timer definitions in 0.625 ms */ +#define HCI_MIN_PAGESCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_PAGESCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_INTERVAL 0x0800 /* 1.28 sec */ + +/* Parameter for pagescan window is passed to LC and is kept in slots */ +#define HCI_MIN_PAGESCAN_WINDOW 0x11 /* 10.625 ms */ +#define HCI_MAX_PAGESCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Inquiryscan timer definitions in 0.625 ms */ +#define HCI_MIN_INQUIRYSCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_INQUIRYSCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_INTERVAL 0x1000 /* 2.56 sec */ + +/* Parameter for inquiryscan window is passed to LC and is kept in slots */ +#define HCI_MIN_INQUIRYSCAN_WINDOW 0x11 /* 10.625 ms */ +#define HCI_MAX_INQUIRYSCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Encryption modes */ +#define HCI_ENCRYPT_MODE_DISABLED 0x00 +#define HCI_ENCRYPT_MODE_POINT_TO_POINT 0x01 +#define HCI_ENCRYPT_MODE_ALL 0x02 + +/* Voice settings */ +#define HCI_INP_CODING_LINEAR 0x0000 /* 0000000000 */ +#define HCI_INP_CODING_U_LAW 0x0100 /* 0100000000 */ +#define HCI_INP_CODING_A_LAW 0x0200 /* 1000000000 */ +#define HCI_INP_CODING_MASK 0x0300 /* 1100000000 */ + +#define HCI_INP_DATA_FMT_1S_COMPLEMENT 0x0000 /* 0000000000 */ +#define HCI_INP_DATA_FMT_2S_COMPLEMENT 0x0040 /* 0001000000 */ +#define HCI_INP_DATA_FMT_SIGN_MAGNITUDE 0x0080 /* 0010000000 */ +#define HCI_INP_DATA_FMT_UNSIGNED 0x00c0 /* 0011000000 */ +#define HCI_INP_DATA_FMT_MASK 0x00c0 /* 0011000000 */ + +#define HCI_INP_SAMPLE_SIZE_8BIT 0x0000 /* 0000000000 */ +#define HCI_INP_SAMPLE_SIZE_16BIT 0x0020 /* 0000100000 */ +#define HCI_INP_SAMPLE_SIZE_MASK 0x0020 /* 0000100000 */ + +#define HCI_INP_LINEAR_PCM_BIT_POS_MASK 0x001c /* 0000011100 */ +#define HCI_INP_LINEAR_PCM_BIT_POS_OFFS 2 + +#define HCI_AIR_CODING_FORMAT_CVSD 0x0000 /* 0000000000 */ +#define HCI_AIR_CODING_FORMAT_U_LAW 0x0001 /* 0000000001 */ +#define HCI_AIR_CODING_FORMAT_A_LAW 0x0002 /* 0000000010 */ +#define HCI_AIR_CODING_FORMAT_TRANSPNT 0x0003 /* 0000000011 */ +#define HCI_AIR_CODING_FORMAT_MASK 0x0003 /* 0000000011 */ + +/* default 0001100000 */ +#define HCI_DEFAULT_VOICE_SETTINGS (HCI_INP_CODING_LINEAR \ + | HCI_INP_DATA_FMT_2S_COMPLEMENT \ + | HCI_INP_SAMPLE_SIZE_16BIT \ + | HCI_AIR_CODING_FORMAT_CVSD) + +#define HCI_CVSD_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_CVSD) +#define HCI_U_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_U_LAW) +#define HCI_A_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_A_LAW) +#define HCI_TRANSPNT_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_TRANSPNT) + +/* Retransmit timer definitions in 0.625 */ +#define HCI_MAX_AUTO_FLUSH_TOUT 0x07FF +#define HCI_DEFAULT_AUTO_FLUSH_TOUT 0 /* No auto flush */ + +/* Broadcast retransmitions */ +#define HCI_DEFAULT_NUM_BCAST_RETRAN 1 + +/* Define broadcast data types as passed in the hci data packet */ +#define HCI_DATA_POINT_TO_POINT 0x00 +#define HCI_DATA_ACTIVE_BCAST 0x01 +#define HCI_DATA_PICONET_BCAST 0x02 + +/* Hold mode activity */ +#define HCI_MAINTAIN_CUR_POWER_STATE 0x00 +#define HCI_SUSPEND_PAGE_SCAN 0x01 +#define HCI_SUSPEND_INQUIRY_SCAN 0x02 +#define HCI_SUSPEND_PERIODIC_INQUIRIES 0x04 + +/* Default Link Supervision timeoout */ +#define HCI_DEFAULT_INACT_TOUT 0x7D00 /* BR/EDR (20 seconds) */ +#define HCI_DEFAULT_AMP_INACT_TOUT 0x3E80 /* AMP (10 seconds) */ + +/* Read transmit power level parameter */ +#define HCI_READ_CURRENT 0x00 +#define HCI_READ_MAXIMUM 0x01 + +/* Link types for connection complete event */ +#define HCI_LINK_TYPE_SCO 0x00 +#define HCI_LINK_TYPE_ACL 0x01 +#define HCI_LINK_TYPE_ESCO 0x02 + +/* Link Key Notification Event (Key Type) definitions */ +#define HCI_LKEY_TYPE_COMBINATION 0x00 +#define HCI_LKEY_TYPE_LOCAL_UNIT 0x01 +#define HCI_LKEY_TYPE_REMOTE_UNIT 0x02 +#define HCI_LKEY_TYPE_DEBUG_COMB 0x03 +#define HCI_LKEY_TYPE_UNAUTH_COMB 0x04 +#define HCI_LKEY_TYPE_AUTH_COMB 0x05 +#define HCI_LKEY_TYPE_CHANGED_COMB 0x06 + +/* Internal definitions - not used over HCI */ +#define HCI_LKEY_TYPE_AMP_WIFI 0x80 +#define HCI_LKEY_TYPE_AMP_UWB 0x81 +#define HCI_LKEY_TYPE_UNKNOWN 0xff + +/* Read Local Version HCI Version return values (Command Complete Event) */ +#define HCI_VERSION_1_0B 0x00 +#define HCI_VERSION_1_1 0x01 + +/* Define an invalid value for a handle */ +#define HCI_INVALID_HANDLE 0xFFFF + +/* Define max ammount of data in the HCI command */ +#define HCI_COMMAND_SIZE 255 + +/* Define the preamble length for all HCI Commands. +** This is 2-bytes for opcode and 1 byte for length +*/ +#define HCIC_PREAMBLE_SIZE 3 + +/* Define the preamble length for all HCI Events +** This is 1-byte for opcode and 1 byte for length +*/ +#define HCIE_PREAMBLE_SIZE 2 +#define HCI_SCO_PREAMBLE_SIZE 3 +#define HCI_DATA_PREAMBLE_SIZE 4 + +/* local Bluetooth controller id for AMP HCI */ +#define LOCAL_BR_EDR_CONTROLLER_ID 0 + +/* controller id types for AMP HCI */ +#define HCI_CONTROLLER_TYPE_BR_EDR 0 +#define HCI_CONTROLLER_TYPE_802_11 1 +#define HCI_CONTROLLER_TYPE_ECMA 2 +#define HCI_MAX_CONTROLLER_TYPES 3 + + + + +/* AMP Controller Status codes +*/ +#define HCI_AMP_CTRLR_PHYSICALLY_DOWN 0 +#define HCI_AMP_CTRLR_USABLE_BY_BT 1 +#define HCI_AMP_CTRLR_UNUSABLE_FOR_BT 2 +#define HCI_AMP_CTRLR_LOW_CAP_FOR_BT 3 +#define HCI_AMP_CTRLR_MED_CAP_FOR_BT 4 +#define HCI_AMP_CTRLR_HIGH_CAP_FOR_BT 5 +#define HCI_AMP_CTRLR_FULL_CAP_FOR_BT 6 + +#define HCI_MAX_AMP_STATUS_TYPES 7 + + +/* Define the extended flow specification fields used by AMP */ +typedef struct +{ + UINT8 id; + UINT8 stype; + UINT16 max_sdu_size; + UINT32 sdu_inter_time; + UINT32 access_latency; + UINT32 flush_timeout; +} tHCI_EXT_FLOW_SPEC; + + +/* HCI message type definitions (for H4 messages) */ +#define HCIT_TYPE_COMMAND 1 +#define HCIT_TYPE_ACL_DATA 2 +#define HCIT_TYPE_SCO_DATA 3 +#define HCIT_TYPE_EVENT 4 +#define HCIT_TYPE_LM_DIAG 7 +#define HCIT_TYPE_NFC 16 + +#define HCIT_LM_DIAG_LENGTH 63 + +/* Parameter information for HCI_BRCM_SET_ACL_PRIORITY */ +#define HCI_BRCM_ACL_PRIORITY_PARAM_SIZE 3 +#define HCI_BRCM_ACL_PRIORITY_LOW 0x00 +#define HCI_BRCM_ACL_PRIORITY_HIGH 0xFF +#define HCI_BRCM_SET_ACL_PRIORITY (0x0057 | HCI_GRP_VENDOR_SPECIFIC) + +/* Define values for LMP Test Control parameters +** Test Scenario, Hopping Mode, Power Control Mode +*/ +#define LMP_TESTCTL_TESTSC_PAUSE 0 +#define LMP_TESTCTL_TESTSC_TXTEST_0 1 +#define LMP_TESTCTL_TESTSC_TXTEST_1 2 +#define LMP_TESTCTL_TESTSC_TXTEST_1010 3 +#define LMP_TESTCTL_TESTSC_PSRND_BITSEQ 4 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_ACL 5 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_SCO 6 +#define LMP_TESTCTL_TESTSC_ACL_NOWHIT 7 +#define LMP_TESTCTL_TESTSC_SCO_NOWHIT 8 +#define LMP_TESTCTL_TESTSC_TXTEST_11110000 9 +#define LMP_TESTCTL_TESTSC_EXITTESTMODE 255 + +#define LMP_TESTCTL_HOPMOD_RXTX1FREQ 0 +#define LMP_TESTCTL_HOPMOD_HOP_EURUSA 1 +#define LMP_TESTCTL_HOPMOD_HOP_JAPAN 2 +#define LMP_TESTCTL_HOPMOD_HOP_FRANCE 3 +#define LMP_TESTCTL_HOPMOD_HOP_SPAIN 4 +#define LMP_TESTCTL_HOPMOD_REDUCED_HOP 5 + +#define LMP_TESTCTL_POWCTL_FIXEDTX_OP 0 +#define LMP_TESTCTL_POWCTL_ADAPTIVE 1 + + +/* +** Define company IDs (from Bluetooth Assigned Numbers v1.1, section 2.2) +*/ +#define LMP_COMPID_ERICSSON 0 +#define LMP_COMPID_NOKIA 1 +#define LMP_COMPID_INTEL 2 +#define LMP_COMPID_IBM 3 +#define LMP_COMPID_TOSHIBA 4 +#define LMP_COMPID_3COM 5 +#define LMP_COMPID_MICROSOFT 6 +#define LMP_COMPID_LUCENT 7 +#define LMP_COMPID_MOTOROLA 8 +#define LMP_COMPID_INFINEON 9 +#define LMP_COMPID_CSR 10 +#define LMP_COMPID_SILICON_WAVE 11 +#define LMP_COMPID_DIGIANSWER 12 +#define LMP_COMPID_TEXAS_INSTRUMENTS 13 +#define LMP_COMPID_PARTHUS 14 +#define LMP_COMPID_BROADCOM 15 +#define LMP_COMPID_MITEL_SEMI 16 +#define LMP_COMPID_WIDCOMM 17 +#define LMP_COMPID_ZEEVO 18 +#define LMP_COMPID_ATMEL 19 +#define LMP_COMPID_MITSUBISHI 20 +#define LMP_COMPID_RTX_TELECOM 21 +#define LMP_COMPID_KC_TECH 22 +#define LMP_COMPID_NEWLOGIC 23 +#define LMP_COMPID_TRANSILICA 24 +#define LMP_COMPID_ROHDE_SCHWARZ 25 +#define LMP_COMPID_TTPCOM 26 +#define LMP_COMPID_SIGNIA 27 +#define LMP_COMPID_CONEXANT 28 +#define LMP_COMPID_QUALCOMM 29 +#define LMP_COMPID_INVENTEL 30 +#define LMP_COMPID_AVM 31 +#define LMP_COMPID_BANDSPEED 32 +#define LMP_COMPID_MANSELLA 33 +#define LMP_COMPID_NEC_CORP 34 +#define LMP_COMPID_WAVEPLUS 35 +#define LMP_COMPID_ALCATEL 36 +#define LMP_COMPID_PHILIPS 37 +#define LMP_COMPID_C_TECHNOLOGIES 38 +#define LMP_COMPID_OPEN_INTERFACE 39 +#define LMP_COMPID_RF_MICRO 40 +#define LMP_COMPID_HITACHI 41 +#define LMP_COMPID_SYMBOL_TECH 42 +#define LMP_COMPID_TENOVIS 43 +#define LMP_COMPID_MACRONIX 44 +#define LMP_COMPID_GCT_SEMI 45 +#define LMP_COMPID_NORWOOD_SYSTEMS 46 +#define LMP_COMPID_MEWTEL_TECH 47 +#define LMP_COMPID_STM 48 +#define LMP_COMPID_SYNOPSYS 49 +#define LMP_COMPID_RED_M_LTD 50 +#define LMP_COMPID_COMMIL_LTD 51 +#define LMP_COMPID_CATC 52 +#define LMP_COMPID_ECLIPSE 53 +#define LMP_COMPID_RENESAS_TECH 54 +#define LMP_COMPID_MOBILIAN_CORP 55 +#define LMP_COMPID_TERAX 56 +#define LMP_COMPID_ISSC 57 +#define LMP_COMPID_MATSUSHITA 58 +#define LMP_COMPID_GENNUM_CORP 59 +#define LMP_COMPID_RESEARCH_IN_MOTION 60 +#define LMP_COMPID_IPEXTREME 61 +#define LMP_COMPID_SYSTEMS_AND_CHIPS 62 +#define LMP_COMPID_BLUETOOTH_SIG 63 +#define LMP_COMPID_SEIKO_EPSON_CORP 64 +#define LMP_COMPID_ISS_TAIWAN 65 +#define LMP_COMPID_CONWISE_TECHNOLOGIES 66 +#define LMP_COMPID_PARROT_SA 67 +#define LMP_COMPID_SOCKET_COMM 68 +#define LMP_COMPID_ALTHEROS 69 +#define LMP_COMPID_MEDIATEK 70 +#define LMP_COMPID_BLUEGIGA 71 +#define LMP_COMPID_MARVELL 72 +#define LMP_COMPID_3DSP_CORP 73 +#define LMP_COMPID_ACCEL_SEMICONDUCTOR 74 +#define LMP_COMPID_CONTINENTAL_AUTO 75 +#define LMP_COMPID_APPLE 76 +#define LMP_COMPID_STACCATO 77 +#define LMP_COMPID_AVAGO_TECHNOLOGIES 78 +#define LMP_COMPID_APT_LTD 79 +#define LMP_COMPID_SIRF_TECHNOLOGY 80 +#define LMP_COMPID_TZERO_TECHNOLOGY 81 +#define LMP_COMPID_J_AND_M_CORP 82 +#define LMP_COMPID_FREE_2_MOVE 83 +#define LMP_COMPID_3DIJOY_CORP 84 +#define LMP_COMPID_PLANTRONICS 85 +#define LMP_COMPID_SONY_ERICSSON_MOBILE 86 +#define LMP_COMPID_HARMON_INTL_IND 87 +#define LMP_COMPID_VIZIO 88 +#define LMP_COMPID_NORDIC SEMI 89 +#define LMP_COMPID_EM MICRO 90 +#define LMP_COMPID_RALINK TECH 91 +#define LMP_COMPID_BELKIN INC 92 +#define LMP_COMPID_REALTEK SEMI 93 +#define LMP_COMPID_STONESTREET ONE 94 +#define LMP_COMPID_WICENTRIC 95 +#define LMP_COMPID_RIVIERAWAVES 96 +#define LMP_COMPID_RDA MICRO 97 +#define LMP_COMPID_GIBSON GUITARS 98 +#define LMP_COMPID_MICOMMAND INC 99 +#define LMP_COMPID_BAND XI 100 +#define LMP_COMPID_HP COMPANY 101 +#define LMP_COMPID_9SOLUTIONS OY 102 +#define LMP_COMPID_GN NETCOM 103 +#define LMP_COMPID_GENERAL MOTORS 104 +#define LMP_COMPID_AD ENGINEERING 105 +#define LMP_COMPID_MINDTREE LTD 106 +#define LMP_COMPID_POLAR ELECTRO 107 +#define LMP_COMPID_BEAUTIFUL ENTERPRISE 108 +#define LMP_COMPID_BRIARTEK 109 +#define LMP_COMPID_SUMMIT DATA COMM 110 +#define LMP_COMPID_SOUND ID 111 +#define LMP_COMPID_MONSTER LLC 112 +#define LMP_COMPID_CONNECTBLU 113 +#define LMP_COMPID_MAX_ID 114 /* this is a place holder */ +#define LMP_COMPID_INTERNAL 65535 + +#define MAX_LMP_COMPID (LMP_COMPID_MAX_ID) +/* +** Define the packet types in the packet header, and a couple extra +*/ +#define PKT_TYPE_NULL 0x00 +#define PKT_TYPE_POLL 0x01 +#define PKT_TYPE_FHS 0x02 +#define PKT_TYPE_DM1 0x03 + +#define PKT_TYPE_DH1 0x04 +#define PKT_TYPE_HV1 0x05 +#define PKT_TYPE_HV2 0x06 +#define PKT_TYPE_HV3 0x07 +#define PKT_TYPE_DV 0x08 +#define PKT_TYPE_AUX1 0x09 + +#define PKT_TYPE_DM3 0x0a +#define PKT_TYPE_DH3 0x0b + +#define PKT_TYPE_DM5 0x0e +#define PKT_TYPE_DH5 0x0f + + +#define PKT_TYPE_ID 0x10 /* Internally used packet types */ +#define PKT_TYPE_BAD 0x11 +#define PKT_TYPE_NONE 0x12 + +/* +** Define packet size +*/ +#define HCI_DM1_PACKET_SIZE 17 +#define HCI_DH1_PACKET_SIZE 27 +#define HCI_DM3_PACKET_SIZE 121 +#define HCI_DH3_PACKET_SIZE 183 +#define HCI_DM5_PACKET_SIZE 224 +#define HCI_DH5_PACKET_SIZE 339 +#define HCI_AUX1_PACKET_SIZE 29 +#define HCI_HV1_PACKET_SIZE 10 +#define HCI_HV2_PACKET_SIZE 20 +#define HCI_HV3_PACKET_SIZE 30 +#define HCI_DV_PACKET_SIZE 9 +#define HCI_EDR2_DH1_PACKET_SIZE 54 +#define HCI_EDR2_DH3_PACKET_SIZE 367 +#define HCI_EDR2_DH5_PACKET_SIZE 679 +#define HCI_EDR3_DH1_PACKET_SIZE 83 +#define HCI_EDR3_DH3_PACKET_SIZE 552 +#define HCI_EDR3_DH5_PACKET_SIZE 1021 + +/* +** Features encoding - page 0 +*/ +#define HCI_NUM_FEATURE_BYTES 8 +#define HCI_FEATURES_KNOWN(x) ((x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7]) != 0) + +#define HCI_FEATURE_3_SLOT_PACKETS_MASK 0x01 +#define HCI_FEATURE_3_SLOT_PACKETS_OFF 0 +#define HCI_3_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_PACKETS_OFF] & HCI_FEATURE_3_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_5_SLOT_PACKETS_MASK 0x02 +#define HCI_FEATURE_5_SLOT_PACKETS_OFF 0 +#define HCI_5_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_PACKETS_OFF] & HCI_FEATURE_5_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_ENCRYPTION_MASK 0x04 +#define HCI_FEATURE_ENCRYPTION_OFF 0 +#define HCI_ENCRYPTION_SUPPORTED(x) ((x)[HCI_FEATURE_ENCRYPTION_OFF] & HCI_FEATURE_ENCRYPTION_MASK) + +#define HCI_FEATURE_SLOT_OFFSET_MASK 0x08 +#define HCI_FEATURE_SLOT_OFFSET_OFF 0 +#define HCI_SLOT_OFFSET_SUPPORTED(x) ((x)[HCI_FEATURE_SLOT_OFFSET_OFF] & HCI_FEATURE_SLOT_OFFSET_MASK) + +#define HCI_FEATURE_TIMING_ACC_MASK 0x10 +#define HCI_FEATURE_TIMING_ACC_OFF 0 +#define HCI_TIMING_ACC_SUPPORTED(x) ((x)[HCI_FEATURE_TIMING_ACC_OFF] & HCI_FEATURE_TIMING_ACC_MASK) + +#define HCI_FEATURE_SWITCH_MASK 0x20 +#define HCI_FEATURE_SWITCH_OFF 0 +#define HCI_SWITCH_SUPPORTED(x) ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK) + +#define HCI_FEATURE_HOLD_MODE_MASK 0x40 +#define HCI_FEATURE_HOLD_MODE_OFF 0 +#define HCI_HOLD_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_HOLD_MODE_OFF] & HCI_FEATURE_HOLD_MODE_MASK) + +#define HCI_FEATURE_SNIFF_MODE_MASK 0x80 +#define HCI_FEATURE_SNIFF_MODE_OFF 0 +#define HCI_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_MODE_OFF] & HCI_FEATURE_SNIFF_MODE_MASK) + +#define HCI_FEATURE_PARK_MODE_MASK 0x01 +#define HCI_FEATURE_PARK_MODE_OFF 1 +#define HCI_PARK_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_PARK_MODE_OFF] & HCI_FEATURE_PARK_MODE_MASK) + +#define HCI_FEATURE_RSSI_MASK 0x02 +#define HCI_FEATURE_RSSI_OFF 1 +#define HCI_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_RSSI_OFF] & HCI_FEATURE_RSSI_MASK) + +#define HCI_FEATURE_CQM_DATA_RATE_MASK 0x04 +#define HCI_FEATURE_CQM_DATA_RATE_OFF 1 +#define HCI_CQM_DATA_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_CQM_DATA_RATE_OFF] & HCI_FEATURE_CQM_DATA_RATE_MASK) + +#define HCI_FEATURE_SCO_LINK_MASK 0x08 +#define HCI_FEATURE_SCO_LINK_OFF 1 +#define HCI_SCO_LINK_SUPPORTED(x) ((x)[HCI_FEATURE_SCO_LINK_OFF] & HCI_FEATURE_SCO_LINK_MASK) + +#define HCI_FEATURE_HV2_PACKETS_MASK 0x10 +#define HCI_FEATURE_HV2_PACKETS_OFF 1 +#define HCI_HV2_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV2_PACKETS_OFF] & HCI_FEATURE_HV2_PACKETS_MASK) + +#define HCI_FEATURE_HV3_PACKETS_MASK 0x20 +#define HCI_FEATURE_HV3_PACKETS_OFF 1 +#define HCI_HV3_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV3_PACKETS_OFF] & HCI_FEATURE_HV3_PACKETS_MASK) + +#define HCI_FEATURE_U_LAW_MASK 0x40 +#define HCI_FEATURE_U_LAW_OFF 1 +#define HCI_LMP_U_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_U_LAW_OFF] & HCI_FEATURE_U_LAW_MASK) + +#define HCI_FEATURE_A_LAW_MASK 0x80 +#define HCI_FEATURE_A_LAW_OFF 1 +#define HCI_LMP_A_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_A_LAW_OFF] & HCI_FEATURE_A_LAW_MASK) + +#define HCI_FEATURE_CVSD_MASK 0x01 +#define HCI_FEATURE_CVSD_OFF 2 +#define HCI_LMP_CVSD_SUPPORTED(x) ((x)[HCI_FEATURE_CVSD_OFF] & HCI_FEATURE_CVSD_MASK) + +#define HCI_FEATURE_PAGING_SCHEME_MASK 0x02 +#define HCI_FEATURE_PAGING_SCHEME_OFF 2 +#define HCI_PAGING_SCHEME_SUPPORTED(x) ((x)[HCI_FEATURE_PAGING_SCHEME_OFF] & HCI_FEATURE_PAGING_SCHEME_MASK) + +#define HCI_FEATURE_POWER_CTRL_MASK 0x04 +#define HCI_FEATURE_POWER_CTRL_OFF 2 +#define HCI_POWER_CTRL_SUPPORTED(x) ((x)[HCI_FEATURE_POWER_CTRL_OFF] & HCI_FEATURE_POWER_CTRL_MASK) + +#define HCI_FEATURE_TRANSPNT_MASK 0x08 +#define HCI_FEATURE_TRANSPNT_OFF 2 +#define HCI_LMP_TRANSPNT_SUPPORTED(x) ((x)[HCI_FEATURE_TRANSPNT_OFF] & HCI_FEATURE_TRANSPNT_MASK) + +#define HCI_FEATURE_FLOW_CTRL_LAG_MASK 0x70 +#define HCI_FEATURE_FLOW_CTRL_LAG_OFF 2 +#define HCI_FLOW_CTRL_LAG_VALUE(x) (((x)[HCI_FEATURE_FLOW_CTRL_LAG_OFF] & HCI_FEATURE_FLOW_CTRL_LAG_MASK) >> 4) + +#define HCI_FEATURE_BROADCAST_ENC_MASK 0x80 +#define HCI_FEATURE_BROADCAST_ENC_OFF 2 +#define HCI_LMP_BCAST_ENC_SUPPORTED(x) ((x)[HCI_FEATURE_BROADCAST_ENC_OFF] & HCI_FEATURE_BROADCAST_ENC_MASK) + +#define HCI_FEATURE_SCATTER_MODE_MASK 0x01 +#define HCI_FEATURE_SCATTER_MODE_OFF 3 +#define HCI_LMP_SCATTER_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SCATTER_MODE_OFF] & HCI_FEATURE_SCATTER_MODE_MASK) + +#define HCI_FEATURE_EDR_ACL_2MPS_MASK 0x02 +#define HCI_FEATURE_EDR_ACL_2MPS_OFF 3 +#define HCI_EDR_ACL_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_2MPS_OFF] & HCI_FEATURE_EDR_ACL_2MPS_MASK) + +#define HCI_FEATURE_EDR_ACL_3MPS_MASK 0x04 +#define HCI_FEATURE_EDR_ACL_3MPS_OFF 3 +#define HCI_EDR_ACL_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_3MPS_OFF] & HCI_FEATURE_EDR_ACL_3MPS_MASK) + +#define HCI_FEATURE_ENHANCED_INQ_MASK 0x08 +#define HCI_FEATURE_ENHANCED_INQ_OFF 3 +#define HCI_ENHANCED_INQ_SUPPORTED(x) ((x)[HCI_FEATURE_ENHANCED_INQ_OFF] & HCI_FEATURE_ENHANCED_INQ_MASK) + +#define HCI_FEATURE_INTERLACED_INQ_SCAN_MASK 0x10 +#define HCI_FEATURE_INTERLACED_INQ_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_INQ_SCAN_OFF] & HCI_FEATURE_INTERLACED_INQ_SCAN_MASK) + +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK 0x20 +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_PAGE_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF] & HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK) + +#define HCI_FEATURE_INQ_RSSI_MASK 0x40 +#define HCI_FEATURE_INQ_RSSI_OFF 3 +#define HCI_LMP_INQ_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RSSI_OFF] & HCI_FEATURE_INQ_RSSI_MASK) + +#define HCI_FEATURE_ESCO_EV3_MASK 0x80 +#define HCI_FEATURE_ESCO_EV3_OFF 3 +#define HCI_ESCO_EV3_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV3_OFF] & HCI_FEATURE_ESCO_EV3_MASK) + +#define HCI_FEATURE_ESCO_EV4_MASK 0x01 +#define HCI_FEATURE_ESCO_EV4_OFF 4 +#define HCI_ESCO_EV4_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV4_OFF] & HCI_FEATURE_ESCO_EV4_MASK) + +#define HCI_FEATURE_ESCO_EV5_MASK 0x02 +#define HCI_FEATURE_ESCO_EV5_OFF 4 +#define HCI_ESCO_EV5_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV5_OFF] & HCI_FEATURE_ESCO_EV5_MASK) + +#define HCI_FEATURE_ABSENCE_MASKS_MASK 0x04 +#define HCI_FEATURE_ABSENCE_MASKS_OFF 4 +#define HCI_LMP_ABSENCE_MASKS_SUPPORTED(x) ((x)[HCI_FEATURE_ABSENCE_MASKS_OFF] & HCI_FEATURE_ABSENCE_MASKS_MASK) + +#define HCI_FEATURE_AFH_CAP_SLAVE_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_SLAVE_OFF 4 +#define HCI_LMP_AFH_CAP_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_SLAVE_OFF] & HCI_FEATURE_AFH_CAP_SLAVE_MASK) + +#define HCI_FEATURE_AFH_CLASS_SLAVE_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_SLAVE_OFF 4 +#define HCI_LMP_AFH_CLASS_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_SLAVE_OFF] & HCI_FEATURE_AFH_CLASS_SLAVE_MASK) + +#define HCI_FEATURE_ALIAS_AUTH_MASK 0x20 +#define HCI_FEATURE_ALIAS_AUTH_OFF 4 +#define HCI_LMP_ALIAS_AUTH_SUPPORTED(x) ((x)[HCI_FEATURE_ALIAS_AUTH_OFF] & HCI_FEATURE_ALIAS_AUTH_MASK) + +#define HCI_FEATURE_ANON_MODE_MASK 0x40 +#define HCI_FEATURE_ANON_MODE_OFF 4 +#define HCI_LMP_ANON_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_ANON_MODE_OFF] & HCI_FEATURE_ANON_MODE_MASK) + +#define HCI_FEATURE_3_SLOT_EDR_ACL_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ACL_OFF 4 +#define HCI_3_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ACL_OFF] & HCI_FEATURE_3_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_5_SLOT_EDR_ACL_MASK 0x01 +#define HCI_FEATURE_5_SLOT_EDR_ACL_OFF 5 +#define HCI_5_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_EDR_ACL_OFF] & HCI_FEATURE_5_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_SNIFF_SUB_RATE_MASK 0x02 +#define HCI_FEATURE_SNIFF_SUB_RATE_OFF 5 +#define HCI_SNIFF_SUB_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_SUB_RATE_OFF] & HCI_FEATURE_SNIFF_SUB_RATE_MASK) + +#define HCI_FEATURE_ATOMIC_ENCRYPT_MASK 0x04 +#define HCI_FEATURE_ATOMIC_ENCRYPT_OFF 5 +#define HCI_ATOMIC_ENCRYPT_SUPPORTED(x) ((x)[HCI_FEATURE_ATOMIC_ENCRYPT_OFF] & HCI_FEATURE_ATOMIC_ENCRYPT_MASK) + +#define HCI_FEATURE_AFH_CAP_MASTR_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_MASTR_OFF 5 +#define HCI_LMP_AFH_CAP_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_MASTR_OFF] & HCI_FEATURE_AFH_CAP_MASTR_MASK) + +#define HCI_FEATURE_AFH_CLASS_MASTR_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_MASTR_OFF 5 +#define HCI_LMP_AFH_CLASS_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_MASTR_OFF] & HCI_FEATURE_AFH_CLASS_MASTR_MASK) + +#define HCI_FEATURE_EDR_ESCO_2MPS_MASK 0x20 +#define HCI_FEATURE_EDR_ESCO_2MPS_OFF 5 +#define HCI_EDR_ESCO_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_2MPS_OFF] & HCI_FEATURE_EDR_ESCO_2MPS_MASK) + +#define HCI_FEATURE_EDR_ESCO_3MPS_MASK 0x40 +#define HCI_FEATURE_EDR_ESCO_3MPS_OFF 5 +#define HCI_EDR_ESCO_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_3MPS_OFF] & HCI_FEATURE_EDR_ESCO_3MPS_MASK) + +#define HCI_FEATURE_3_SLOT_EDR_ESCO_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ESCO_OFF 5 +#define HCI_3_SLOT_EDR_ESCO_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ESCO_OFF] & HCI_FEATURE_3_SLOT_EDR_ESCO_MASK) + +#define HCI_FEATURE_EXT_INQ_RSP_MASK 0x01 +#define HCI_FEATURE_EXT_INQ_RSP_OFF 6 +#define HCI_EXT_INQ_RSP_SUPPORTED(x) ((x)[HCI_FEATURE_EXT_INQ_RSP_OFF] & HCI_FEATURE_EXT_INQ_RSP_MASK) + +#define HCI_FEATURE_ANUM_PIN_AWARE_MASK 0x02 +#define HCI_FEATURE_ANUM_PIN_AWARE_OFF 6 +#define HCI_ANUM_PIN_AWARE_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_AWARE_OFF] & HCI_FEATURE_ANUM_PIN_AWARE_MASK) + +#define HCI_FEATURE_ANUM_PIN_CAP_MASK 0x04 +#define HCI_FEATURE_ANUM_PIN_CAP_OFF 6 +#define HCI_ANUM_PIN_CAP_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_CAP_OFF] & HCI_FEATURE_ANUM_PIN_CAP_MASK) + +#define HCI_FEATURE_SIMPLE_PAIRING_MASK 0x08 +#define HCI_FEATURE_SIMPLE_PAIRING_OFF 6 +#define HCI_SIMPLE_PAIRING_SUPPORTED(x) ((x)[HCI_FEATURE_SIMPLE_PAIRING_OFF] & HCI_FEATURE_SIMPLE_PAIRING_MASK) + +#define HCI_FEATURE_ENCAP_PDU_MASK 0x10 +#define HCI_FEATURE_ENCAP_PDU_OFF 6 +#define HCI_ENCAP_PDU_SUPPORTED(x) ((x)[HCI_FEATURE_ENCAP_PDU_OFF] & HCI_FEATURE_ENCAP_PDU_MASK) + +#define HCI_FEATURE_ERROR_DATA_MASK 0x20 +#define HCI_FEATURE_ERROR_DATA_OFF 6 +#define HCI_ERROR_DATA_SUPPORTED(x) ((x)[HCI_FEATURE_ERROR_DATA_OFF] & HCI_FEATURE_ERROR_DATA_MASK) + +#define HCI_FEATURE_NON_FLUSHABLE_PB_MASK 0x40 +#define HCI_FEATURE_NON_FLUSHABLE_PB_OFF 6 + +// btla-specific ++ +#ifdef ANDROID_APP_INCLUDED +/* This feature is causing frequent link drops when doing call switch with certain av/hfp headsets */ +#define HCI_NON_FLUSHABLE_PB_SUPPORTED(x) (0)//((x)[HCI_FEATURE_NON_FLUSHABLE_PB_OFF] & HCI_FEATURE_NON_FLUSHABLE_PB_MASK) +#else +#define HCI_NON_FLUSHABLE_PB_SUPPORTED(x) ((x)[HCI_FEATURE_NON_FLUSHABLE_PB_OFF] & HCI_FEATURE_NON_FLUSHABLE_PB_MASK) +#endif +// btla-specific -- + +#define HCI_FEATURE_LINK_SUP_TO_EVT_MASK 0x01 +#define HCI_FEATURE_LINK_SUP_TO_EVT_OFF 7 +#define HCI_LINK_SUP_TO_EVT_SUPPORTED(x) ((x)[HCI_FEATURE_LINK_SUP_TO_EVT_OFF] & HCI_FEATURE_LINK_SUP_TO_EVT_MASK) + +#define HCI_FEATURE_INQ_RESP_TX_MASK 0x02 +#define HCI_FEATURE_INQ_RESP_TX_OFF 7 +#define HCI_INQ_RESP_TX_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RESP_TX_OFF] & HCI_FEATURE_INQ_RESP_TX_MASK) + +#define HCI_FEATURE_EXTENDED_MASK 0x80 +#define HCI_FEATURE_EXTENDED_OFF 7 +#define HCI_LMP_EXTENDED_SUPPORTED(x) ((x)[HCI_FEATURE_EXTENDED_OFF] & HCI_FEATURE_EXTENDED_MASK) + +/* +** Features encoding - page 1 +*/ +#define HCI_EXT_FEATURE_SSP_HOST_MASK 0x01 +#define HCI_EXT_FEATURE_SSP_HOST_OFF 0 +#define HCI_SSP_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SSP_HOST_OFF] & HCI_EXT_FEATURE_SSP_HOST_MASK) + +/* +** Local Supported Commands encoding +*/ +#define HCI_NUM_SUPP_COMMANDS_BYTES 64 + +#define HCI_SUPP_COMMANDS_INQUIRY_MASK 0x01 +#define HCI_SUPP_COMMANDS_INQUIRY_OFF 0 +#define HCI_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_OFF] & HCI_SUPP_COMMANDS_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK 0x02 +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF 0 +#define HCI_INQUIRY_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF] & HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK) + +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK 0x04 +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF 0 +#define HCI_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF 0 +#define HCI_EXIT_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_CONN_OFF 0 +#define HCI_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_MASK 0x20 +#define HCI_SUPP_COMMANDS_DISCONNECT_OFF 0 +#define HCI_DISCONNECT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_MASK) + +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK 0x40 +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF 0 +#define HCI_ADD_SCO_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF] & HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK 0x80 +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF 0 +#define HCI_CANCEL_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK 0x01 +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF 1 +#define HCI_ACCEPT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK 0x02 +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF 1 +#define HCI_REJECT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK 0x20 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK 0x40 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF 1 +#define HCI_CHANGE_CONN_PKT_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK 0x80 +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF 1 +#define HCI_AUTH_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF] & HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF 2 +#define HCI_SET_CONN_ENCRYPTION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF] & HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK 0x02 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF 2 +#define HCI_CHANGE_CONN_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF 2 +#define HCI_MASTER_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK 0x08 +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK 0x10 +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_CANCEL_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF 2 +#define HCI_READ_REMOTE_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF 2 +#define HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF 2 +#define HCI_READ_REMOTE_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF 3 +#define HCI_READ_CLOCK_OFFSET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF] & HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK) + +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF 3 +#define HCI_READ_LMP_HANDLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF] & HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK) + +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK 0x02 +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF 4 +#define HCI_HOLD_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK 0x04 +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF 4 +#define HCI_SNIFF_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF 4 +#define HCI_EXIT_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF] & HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK) + +#define HCI_SUPP_COMMANDS_PARK_STATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_PARK_STATE_OFF 4 +#define HCI_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK 0x20 +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF 4 +#define HCI_EXIT_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_QOS_SETUP_MASK 0x40 +#define HCI_SUPP_COMMANDS_QOS_SETUP_OFF 4 +#define HCI_QOS_SETUP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_QOS_SETUP_OFF] & HCI_SUPP_COMMANDS_QOS_SETUP_MASK) + +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK 0x80 +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF 4 +#define HCI_ROLE_DISCOVERY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF] & HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK) + +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK 0x01 +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF 5 +#define HCI_SWITCH_ROLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF] & HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF 5 +#define HCI_READ_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK 0x04 +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_READ_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK 0x20 +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF 5 +#define HCI_FLOW_SPECIFICATION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF] & HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK 0x40 +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF 5 +#define HCI_SET_EVENT_MASK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK) + +#define HCI_SUPP_COMMANDS_RESET_MASK 0x80 +#define HCI_SUPP_COMMANDS_RESET_OFF 5 +#define HCI_RESET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_OFF] & HCI_SUPP_COMMANDS_RESET_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF 6 +#define HCI_SET_EVENT_FILTER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK) + +#define HCI_SUPP_COMMANDS_FLUSH_MASK 0x02 +#define HCI_SUPP_COMMANDS_FLUSH_OFF 6 +#define HCI_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLUSH_OFF] & HCI_SUPP_COMMANDS_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF 6 +#define HCI_READ_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF 6 +#define HCI_WRITE_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF 6 +#define HCI_CREATE_NEW_UNIT_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF] & HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK) + +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF 6 +#define HCI_READ_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF 6 +#define HCI_WRITE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK 0x80 +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF 6 +#define HCI_DELETE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF 7 +#define HCI_WRITE_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF 7 +#define HCI_READ_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_READ_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_WRITE_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF 7 +#define HCI_READ_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF 7 +#define HCI_WRITE_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF 7 +#define HCI_READ_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF 7 +#define HCI_WRITE_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF 8 +#define HCI_READ_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF 8 +#define HCI_WRITE_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF 8 +#define HCI_READ_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF 8 +#define HCI_WRITE_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF 9 +#define HCI_READ_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF 9 +#define HCI_WRITE_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF 9 +#define HCI_READ_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF 9 +#define HCI_WRITE_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_READ_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_WRITE_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF 9 +#define HCI_READ_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF 9 +#define HCI_WRITE_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_READ_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_WRITE_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF 10 +#define HCI_READ_TRANS_PWR_LEVEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF] & HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK) + +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_READ_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_WRITE_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK 0x20 +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF 10 +#define HCI_SET_HOST_CTRLR_TO_HOST_FC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF] & HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK) + +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK 0x40 +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF 10 +#define HCI_HOST_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK 0x80 +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF 10 +#define HCI_HOST_NUM_COMPLETED_PKTS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF] & HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF 11 +#define HCI_READ_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF 11 +#define HCI_WRITE_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF 11 +#define HCI_READ_NUM_SUPP_IAC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF] & HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK) + +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF 11 +#define HCI_READ_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF 11 +#define HCI_WRITE_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_WRITE_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF 12 +#define HCI_WRITE_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK 0x02 +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF 12 +#define HCI_SET_AFH_CHNL_CLASS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF] & HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_READ_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_WRITE_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF 12 +#define HCI_READ_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF 12 +#define HCI_WRITE_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF 13 +#define HCI_READ_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF 13 +#define HCI_WRITE_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_READ_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_WRITE_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF 14 +#define HCI_READ_LOCAL_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF 14 +#define HCI_READ_LOCAL_SUP_CMDS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF 14 +#define HCI_READ_LOCAL_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF 14 +#define HCI_READ_LOCAL_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF 14 +#define HCI_READ_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF 15 +#define HCI_READ_COUNTRY_CODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF] & HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF 15 +#define HCI_READ_BD_ADDR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF] & HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK) + +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_READ_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK 0x08 +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_RESET_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK 0x10 +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF 15 +#define HCI_GET_LINK_QUALITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF] & HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_RSSI_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_RSSI_OFF 15 +#define HCI_READ_RSSI_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_RSSI_OFF] & HCI_SUPP_COMMANDS_READ_RSSI_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF 15 +#define HCI_READ_AFH_CH_MAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF 15 +#define HCI_READ_BD_CLOCK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF] & HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF 16 +#define HCI_READ_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF 16 +#define HCI_WRITE_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK 0x04 +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF 16 +#define HCI_ENABLE_DEV_UNDER_TEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF] & HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK) + +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK 0x08 +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF 16 +#define HCI_SETUP_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF 16 +#define HCI_ACCEPT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK 0x20 +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF 16 +#define HCI_REJECT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF 17 +#define HCI_READ_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF 17 +#define HCI_WRITE_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF 17 +#define HCI_REFRESH_ENCRYPTION_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF] & HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK) + +/* Octet 17, bit 3 is reserved */ + +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF 17 +#define HCI_SNIFF_SUB_RATE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF] & HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK) + +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_READ_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_WRITE_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF 17 +#define HCI_READ_LOCAL_OOB_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_READ_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_WRITE_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_MASK 0x80 +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_OFF 18 +#define HCI_IO_CAPABILITY_RESPONSE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_OFF] & HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK 0x01 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK 0x02 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF 19 +#define HCI_WRITE_SIMPLE_PAIRING_DBG_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK 0x40 +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF 19 +#define HCI_ENHANCED_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF] & HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK 0x80 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK) + +/* Supported Commands (Byte 20) */ +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK 0x04 +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF 20 +#define HCI_SEND_NOTIF_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF] & HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF 20 +#define HCI_IO_CAP_REQ_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_OFF 20 +#define HCI_READ_ENCR_KEY_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_ENCR_KEY_SIZE_MASK) + +/* Supported Commands (Byte 21) */ +#define HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_MASK 0x01 +#define HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_OFF 21 +#define HCI_CREATE_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_CREATE_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_MASK 0x02 +#define HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_OFF 21 +#define HCI_ACCEPT_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_ACCEPT_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_MASK 0x04 +#define HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_OFF 21 +#define HCI_DISCONNECT_PHYSICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_PHYSICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_MASK 0x08 +#define HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_OFF 21 +#define HCI_CREATE_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_CREATE_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_MASK 0x10 +#define HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_OFF 21 +#define HCI_ACCEPT_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_ACCEPT_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_MASK 0x20 +#define HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_OFF 21 +#define HCI_DISCONNECT_LOGICAL_LINK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_LOGICAL_LINK_MASK) + +#define HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_MASK 0x40 +#define HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_OFF 21 +#define HCI_LOGICAL_LINK_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_OFF] & HCI_SUPP_COMMANDS_LOGICAL_LINK_CANCEL_MASK) + +#define HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_MASK 0x80 +#define HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_OFF 21 +#define HCI_FLOW_SPEC_MODIFY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_OFF] & HCI_SUPP_COMMANDS_FLOW_SPEC_MODIFY_MASK) + +/* Supported Commands (Byte 22) */ +#define HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF 22 +#define HCI_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF] & HCI_SUPP_COMMANDS_READ_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF 22 +#define HCI_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_MASK 0x04 +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_OFF 22 +#define HCI_SET_EVENT_MASK_PAGE_2_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_MASK_PAGE_2_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCATION_DATA_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_LOCATION_DATA_OFF 22 +#define HCI_READ_LOCATION_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCATION_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCATION_DATA_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_OFF 22 +#define HCI_WRITE_LOCATION_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_OFF] & HCI_SUPP_COMMANDS_WRITE_LOCATION_DATA_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_OFF 22 +#define HCI_READ_LOCAL_AMP_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_AMP_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_OFF 22 +#define HCI_READ_LOCAL_AMP_ASSOC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_AMP_ASSOC_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_OFF 22 +#define HCI_WRITE_REMOTE_AMP_ASSOC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_OFF] & HCI_SUPP_COMMANDS_WRITE_REMOTE_AMP_ASSOC_MASK) + +/* Supported Commands (Byte 23) */ +#define HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_OFF 23 +#define HCI_READ_FLOW_CONTROL_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_OFF] & HCI_SUPP_COMMANDS_READ_FLOW_CONTROL_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_OFF 23 +#define HCI_WRITE_FLOW_CONTROL_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_FLOW_CONTROL_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_OFF 23 +#define HCI_READ_DATA_BLOCK_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_DATA_BLOCK_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_MASK 0x20 +#define HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_OFF 23 +#define HCI_ENABLE_AMP_RCVR_REPORTS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_OFF] & HCI_SUPP_COMMANDS_ENABLE_AMP_RCVR_REPORTS_MASK) + +#define HCI_SUPP_COMMANDS_AMP_TEST_END_MASK 0x40 +#define HCI_SUPP_COMMANDS_AMP_TEST_END_OFF 23 +#define HCI_AMP_TEST_END_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AMP_TEST_END_OFF] & HCI_SUPP_COMMANDS_AMP_TEST_END_MASK) + +#define HCI_SUPP_COMMANDS_AMP_TEST_MASK 0x80 +#define HCI_SUPP_COMMANDS_AMP_TEST_OFF 23 +#define HCI_AMP_TEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AMP_TEST_OFF] & HCI_SUPP_COMMANDS_AMP_TEST_MASK) + +/* Supported Commands (Byte 24) */ +#define HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_OFF 24 +#define HCI_READ_TRANSMIT_POWER_LEVEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_OFF] & HCI_SUPP_COMMANDS_READ_TRANSMIT_POWER_LEVEL_MASK) + +#define HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_OFF 24 +#define HCI_READ_BE_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_BE_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_OFF 24 +#define HCI_WRITE_BE_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_BE_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_MASK 0x10 +#define HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_OFF 24 +#define HCI_SHORT_RANGE_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_OFF] & HCI_SUPP_COMMANDS_SHORT_RANGE_MODE_MASK) + +/* LE commands TBD +** Supported Commands (Byte 24 continued) +** Supported Commands (Byte 25) +** Supported Commands (Byte 26) +** Supported Commands (Byte 27) +** Supported Commands (Byte 28) +*/ + +/* +Commands of HCI_GRP_VENDOR_SPECIFIC group for WIDCOMM SW LM Simulator +*/ +#ifdef _WIDCOMM + +#define HCI_SET_HCI_TRACE (0x0001 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_SET_LM_TRACE (0x0002 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_WRITE_COUNTRY_CODE (0x0004 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_READ_LM_HISTORY (0x0005 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_WRITE_BD_ADDR (0x0006 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DISABLE_ENCRYPTION (0x0007 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DISABLE_AUTHENTICATION (0x0008 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_GENERIC_LC_CMD (0x000A | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_INCR_POWER (0x000B | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DECR_POWER (0x000C | HCI_GRP_VENDOR_SPECIFIC) + +/* Definitions for the local transactions */ +#define LM_DISCONNECT (0x00D0 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_AUTHENTICATION_REQUESTED (0x00D1 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_SET_CONN_ENCRYPTION (0x00D2 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_START_ENCRYPT_KEY_SIZE (0x00D3 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_START_ENCRYPTION (0x00D4 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_STOP_ENCRYPTION (0x00D5 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_CHANGE_CONN_PACKET_TYPE (0x00D6 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_RMT_NAME_REQUEST (0x00D7 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_READ_RMT_FEATURES (0x00D8 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_READ_RMT_VERSION_INFO (0x00D9 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_READ_RMT_TIMING_INFO (0x00DA | HCI_GRP_VENDOR_SPECIFIC) +#define LM_READ_RMT_CLOCK_OFFSET (0x00DB | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HOLD_MODE (0x00DC | HCI_GRP_VENDOR_SPECIFIC) +#define LM_EXIT_PARK_MODE (0x00DD | HCI_GRP_VENDOR_SPECIFIC) + +#define LM_SCO_LINK_REQUEST (0x00E0 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_SCO_CHANGE (0x00E4 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_SCO_REMOVE (0x00E8 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_MAX_SLOTS (0x00F1 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_MAX_SLOTS_REQUEST (0x00F2 | HCI_GRP_VENDOR_SPECIFIC) + +#ifdef INCLUDE_OPTIONAL_PAGING_SCHEME +#define LM_OPTIONAL_PAGE_REQUEST (0x00F3 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_OPTIONAL_PAGESCAN_REQUEST (0x00F4 | HCI_GRP_VENDOR_SPECIFIC) +#endif + +#define LM_SETUP_COMPLETE (0x00FF | HCI_GRP_VENDOR_SPECIFIC) + +#define LM_HIST_SEND_LMP_FRAME (0x0100 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_RECV_LMP_FRAME (0x0101 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_HCIT_ERROR (0x0102 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_PER_INQ_TOUT (0x0103 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_INQ_SCAN_TOUT (0x0104 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_PAGE_SCAN_TOUT (0x0105 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_RESET_TOUT (0x0106 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_MANDAT_PSCAN_TOUT (0x0107 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_START_TRANS (0x0108 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_HOST_REPLY (0x0109 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_TIMEOUT (0x010A | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_TX_COMP (0x010B | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_HCID_SUSPENDED (0x010C | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_ACL_FAILED (0x010D | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_HCI_COMMAND (0x010E | HCI_GRP_VENDOR_SPECIFIC) + +#define LM_HIST_HCI_EVENT (0x010F | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_HCI_UPDATA (0x0110 | HCI_GRP_VENDOR_SPECIFIC) +#define LM_HIST_HCI_DNDATA (0x0111 | HCI_GRP_VENDOR_SPECIFIC) + +#define HCI_ENTER_TEST_MODE (0x0300 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_LMP_TEST_CNTRL (0x0301 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DEBUG_LC_CMD_MIN (0x0300 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DEBUG_LC_CMD_MAX (0x03FF | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_DEBUG_LC_COMMAND HCI_DEBUG_LC_CMD_MAX + +#endif + + +/* AMP VSE events +*/ +#define AMP_VSE_CHANSPEC_CHAN_MASK 0x00ff + +#define AMP_VSE_CHANSPEC_CTL_SB_MASK 0x0300 +#define AMP_VSE_CHANSPEC_CTL_SB_LOWER 0x0100 +#define AMP_VSE_CHANSPEC_CTL_SB_UPPER 0x0200 +#define AMP_VSE_CHANSPEC_CTL_SB_NONE 0x0300 + +#define AMP_VSE_CHANSPEC_BW_MASK 0x0C00 +#define AMP_VSE_CHANSPEC_BW_10 0x0400 +#define AMP_VSE_CHANSPEC_BW_20 0x0800 +#define AMP_VSE_CHANSPEC_BW_40 0x0C00 + +#define AMP_VSE_CHANSPEC_BAND_MASK 0xf000 +#define AMP_VSE_CHANSPEC_BAND_5G 0x1000 +#define AMP_VSE_CHANSPEC_BAND_2G 0x2000 + + +#endif + diff --git a/stack/include/hcimsgs.h b/stack/include/hcimsgs.h new file mode 100644 index 0000000..3a23dbe --- /dev/null +++ b/stack/include/hcimsgs.h @@ -0,0 +1,1331 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef HCIMSGS_H +#define HCIMSGS_H + +#include "bt_target.h" +#include "hcidefs.h" +#include "bt_types.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Message by message.... */ + +#define HCIC_GET_UINT8(p, off) (UINT8)(*((UINT8 *)((p) + 1) + p->offset + 3 + (off))) + +#define HCIC_GET_UINT16(p, off) (UINT16)((*((UINT8 *)((p) + 1) + p->offset + 3 + (off)) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3 + (off) + 1) << 8))) + +#define HCIC_GET_UINT32(p, off) (UINT32)((*((UINT8 *)((p) + 1) + p->offset + 3 + (off)) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3 + (off) + 1) << 8) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3 + (off) + 2) << 16) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3 + (off) + 3) << 24))) + +#define HCIC_GET_ARRAY(p, off, x, len) \ +{ \ + UINT8 *qq = ((UINT8 *)((p) + 1) + p->offset + 3 + (off)); UINT8 *rr = (UINT8 *)x; \ + int ii; for (ii = 0; ii < len; ii++) *rr++ = *qq++; \ +} + +#define HCIC_GET_ARRAY16(p, off, x) \ +{ \ + UINT8 *qq = ((UINT8 *)((p) + 1) + p->offset + 3 + (off)); UINT8 *rr = (UINT8 *)x + 15; \ + int ii; for (ii = 0; ii < 16; ii++) *rr-- = *qq++; \ +} + +#define HCIC_GET_BDADDR(p, off, x) \ +{ \ + UINT8 *qq = ((UINT8 *)((p) + 1) + p->offset + 3 + (off)); UINT8 *rr = (UINT8 *)x + BD_ADDR_LEN - 1; \ + int ii; for (ii = 0; ii < BD_ADDR_LEN; ii++) *rr-- = *qq++; \ +} + +#define HCIC_GET_DEVCLASS(p, off, x) \ +{ \ + UINT8 *qq = ((UINT8 *)((p) + 1) + p->offset + 3 + (off)); UINT8 *rr = (UINT8 *)x + DEV_CLASS_LEN - 1; \ + int ii; for (ii = 0; ii < DEV_CLASS_LEN; ii++) *rr-- = *qq++; \ +} + +#define HCIC_GET_LAP(p, off, x) \ +{ \ + UINT8 *qq = ((UINT8 *)((p) + 1) + p->offset + 3 + (off)); UINT8 *rr = (UINT8 *)x + LAP_LEN - 1; \ + int ii; for (ii = 0; ii < LAP_LEN; ii++) *rr-- = *qq++; \ +} + +#define HCIC_GET_POINTER(p, off) ((UINT8 *)((p) + 1) + p->offset + 3 + (off)) + + +HCI_API extern BOOLEAN btsnd_hcic_inquiry(const LAP inq_lap, UINT8 duration, + UINT8 response_cnt); + +#define HCIC_PARAM_SIZE_INQUIRY 5 + + +#define HCIC_INQ_INQ_LAP_OFF 0 +#define HCIC_INQ_DUR_OFF 3 +#define HCIC_INQ_RSP_CNT_OFF 4 + /* Inquiry */ + + /* Inquiry Cancel */ +HCI_API extern BOOLEAN btsnd_hcic_inq_cancel(void); + +#define HCIC_PARAM_SIZE_INQ_CANCEL 0 + + /* Periodic Inquiry Mode */ +HCI_API extern BOOLEAN btsnd_hcic_per_inq_mode(UINT16 max_period, UINT16 min_period, + const LAP inq_lap, UINT8 duration, + UINT8 response_cnt); + +#define HCIC_PARAM_SIZE_PER_INQ_MODE 9 + +#define HCI_PER_INQ_MAX_INTRVL_OFF 0 +#define HCI_PER_INQ_MIN_INTRVL_OFF 2 +#define HCI_PER_INQ_INQ_LAP_OFF 4 +#define HCI_PER_INQ_DURATION_OFF 7 +#define HCI_PER_INQ_RSP_CNT_OFF 8 + /* Periodic Inquiry Mode */ + + /* Exit Periodic Inquiry Mode */ +HCI_API extern BOOLEAN btsnd_hcic_exit_per_inq(void); + +#define HCIC_PARAM_SIZE_EXIT_PER_INQ 0 + /* Create Connection */ +HCI_API extern BOOLEAN btsnd_hcic_create_conn(BD_ADDR dest, UINT16 packet_types, + UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, + UINT16 clock_offset, + UINT8 allow_switch); + +#define HCIC_PARAM_SIZE_CREATE_CONN 13 + +#define HCIC_CR_CONN_BD_ADDR_OFF 0 +#define HCIC_CR_CONN_PKT_TYPES_OFF 6 +#define HCIC_CR_CONN_REP_MODE_OFF 8 +#define HCIC_CR_CONN_PAGE_SCAN_MODE_OFF 9 +#define HCIC_CR_CONN_CLK_OFF_OFF 10 +#define HCIC_CR_CONN_ALLOW_SWITCH_OFF 12 + /* Create Connection */ + + /* Disconnect */ +HCI_API extern BOOLEAN btsnd_hcic_disconnect(UINT16 handle, UINT8 reason); + +#define HCIC_PARAM_SIZE_DISCONNECT 3 + +#define HCI_DISC_HANDLE_OFF 0 +#define HCI_DISC_REASON_OFF 2 + /* Disconnect */ + +#if BTM_SCO_INCLUDED == TRUE + /* Add SCO Connection */ +HCI_API extern BOOLEAN btsnd_hcic_add_SCO_conn (UINT16 handle, UINT16 packet_types); +#endif /* BTM_SCO_INCLUDED */ + +#define HCIC_PARAM_SIZE_ADD_SCO_CONN 4 + +#define HCI_ADD_SCO_HANDLE_OFF 0 +#define HCI_ADD_SCO_PACKET_TYPES_OFF 2 + /* Add SCO Connection */ + + /* Create Connection Cancel */ +HCI_API extern BOOLEAN btsnd_hcic_create_conn_cancel(BD_ADDR dest); + +#define HCIC_PARAM_SIZE_CREATE_CONN_CANCEL 6 + +#define HCIC_CR_CONN_CANCEL_BD_ADDR_OFF 0 + /* Create Connection Cancel */ + + /* Accept Connection Request */ +HCI_API extern BOOLEAN btsnd_hcic_accept_conn (BD_ADDR bd_addr, UINT8 role); + +#define HCIC_PARAM_SIZE_ACCEPT_CONN 7 + +#define HCI_ACC_CONN_BD_ADDR_OFF 0 +#define HCI_ACC_CONN_ROLE_OFF 6 + /* Accept Connection Request */ + + /* Reject Connection Request */ +HCI_API extern BOOLEAN btsnd_hcic_reject_conn (BD_ADDR bd_addr, UINT8 reason); + +#define HCIC_PARAM_SIZE_REJECT_CONN 7 + +#define HCI_REJ_CONN_BD_ADDR_OFF 0 +#define HCI_REJ_CONN_REASON_OFF 6 + /* Reject Connection Request */ + + /* Link Key Request Reply */ +HCI_API extern BOOLEAN btsnd_hcic_link_key_req_reply (BD_ADDR bd_addr, + LINK_KEY link_key); + +#define HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY 22 + +#define HCI_LINK_KEY_REPLY_BD_ADDR_OFF 0 +#define HCI_LINK_KEY_REPLY_LINK_KEY_OFF 6 + /* Link Key Request Reply */ + + /* Link Key Request Neg Reply */ +HCI_API extern BOOLEAN btsnd_hcic_link_key_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_LINK_KEY_NEG_REPLY 6 + +#define HCI_LINK_KEY_NEG_REP_BD_ADR_OFF 0 + /* Link Key Request Neg Reply */ + + /* PIN Code Request Reply */ +HCI_API extern BOOLEAN btsnd_hcic_pin_code_req_reply (BD_ADDR bd_addr, + UINT8 pin_code_len, + PIN_CODE pin_code); + +#define HCIC_PARAM_SIZE_PIN_CODE_REQ_REPLY 23 + +#define HCI_PIN_CODE_REPLY_BD_ADDR_OFF 0 +#define HCI_PIN_CODE_REPLY_PIN_LEN_OFF 6 +#define HCI_PIN_CODE_REPLY_PIN_CODE_OFF 7 + /* PIN Code Request Reply */ + + /* Link Key Request Neg Reply */ +HCI_API extern BOOLEAN btsnd_hcic_pin_code_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_PIN_CODE_NEG_REPLY 6 + +#define HCI_PIN_CODE_NEG_REP_BD_ADR_OFF 0 + /* Link Key Request Neg Reply */ + + /* Change Connection Type */ +HCI_API extern BOOLEAN btsnd_hcic_change_conn_type (UINT16 handle, UINT16 packet_types); + +#define HCIC_PARAM_SIZE_CHANGE_CONN_TYPE 4 + +#define HCI_CHNG_PKT_TYPE_HANDLE_OFF 0 +#define HCI_CHNG_PKT_TYPE_PKT_TYPE_OFF 2 + /* Change Connection Type */ + +#define HCIC_PARAM_SIZE_CMD_HANDLE 2 + +#define HCI_CMD_HANDLE_HANDLE_OFF 0 + +HCI_API extern BOOLEAN btsnd_hcic_auth_request (UINT16 handle); /* Authentication Request */ + + /* Set Connection Encryption */ +HCI_API extern BOOLEAN btsnd_hcic_set_conn_encrypt (UINT16 handle, BOOLEAN enable); +#define HCIC_PARAM_SIZE_SET_CONN_ENCRYPT 3 + + +#define HCI_SET_ENCRYPT_HANDLE_OFF 0 +#define HCI_SET_ENCRYPT_ENABLE_OFF 2 + /* Set Connection Encryption */ + +HCI_API extern BOOLEAN btsnd_hcic_change_link_key (UINT16 handle); /* Change Connection Link Key */ + + /* Master Link Key */ +HCI_API extern BOOLEAN btsnd_hcic_master_link_key (BOOLEAN key_flag); + +#define HCIC_PARAM_SIZE_MASTER_LINK_KEY 1 + +#define HCI_MASTER_KEY_FLAG_OFF 0 + /* Master Link Key */ + + /* Remote Name Request */ +HCI_API extern BOOLEAN btsnd_hcic_rmt_name_req (BD_ADDR bd_addr, + UINT8 page_scan_rep_mode, + UINT8 page_scan_mode, + UINT16 clock_offset); + +#define HCIC_PARAM_SIZE_RMT_NAME_REQ 10 + +#define HCI_RMT_NAME_BD_ADDR_OFF 0 +#define HCI_RMT_NAME_REP_MODE_OFF 6 +#define HCI_RMT_NAME_PAGE_SCAN_MODE_OFF 7 +#define HCI_RMT_NAME_CLK_OFF_OFF 8 + /* Remote Name Request */ + + /* Remote Name Request Cancel */ +HCI_API extern BOOLEAN btsnd_hcic_rmt_name_req_cancel(BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_RMT_NAME_REQ_CANCEL 6 + +#define HCI_RMT_NAME_CANCEL_BD_ADDR_OFF 0 + /* Remote Name Request Cancel */ + +HCI_API extern BOOLEAN btsnd_hcic_rmt_features_req(UINT16 handle); /* Remote Features Request */ + + /* Remote Extended Features */ +HCI_API extern BOOLEAN btsnd_hcic_rmt_ext_features(UINT16 handle, UINT8 page_num); + +#define HCIC_PARAM_SIZE_RMT_EXT_FEATURES 3 + +#define HCI_RMT_EXT_FEATURES_HANDLE_OFF 0 +#define HCI_RMT_EXT_FEATURES_PAGE_NUM_OFF 2 + /* Remote Extended Features */ + + + /* Local Extended Features */ +HCI_API extern BOOLEAN btsnd_hcic_read_local_ext_features (UINT8 page_num); + +#define HCIC_PARAM_SIZE_LOCAL_EXT_FEATURES 1 + +#define HCI_LOCAL_EXT_FEATURES_PAGE_NUM_OFF 0 + /* Local Extended Features */ + + +HCI_API extern BOOLEAN btsnd_hcic_rmt_ver_req(UINT16 handle); /* Remote Version Info Request */ +HCI_API extern BOOLEAN btsnd_hcic_read_rmt_clk_offset(UINT16 handle); /* Remote Clock Offset */ +HCI_API extern BOOLEAN btsnd_hcic_read_lmp_handle(UINT16 handle); /* Remote LMP Handle */ + +HCI_API extern BOOLEAN btsnd_hcic_setup_esco_conn (UINT16 handle, + UINT32 tx_bw, UINT32 rx_bw, + UINT16 max_latency, UINT16 voice, + UINT8 retrans_effort, + UINT16 packet_types); +#define HCIC_PARAM_SIZE_SETUP_ESCO 17 + +#define HCI_SETUP_ESCO_HANDLE_OFF 0 +#define HCI_SETUP_ESCO_TX_BW_OFF 2 +#define HCI_SETUP_ESCO_RX_BW_OFF 6 +#define HCI_SETUP_ESCO_MAX_LAT_OFF 10 +#define HCI_SETUP_ESCO_VOICE_OFF 12 +#define HCI_SETUP_ESCO_RETRAN_EFF_OFF 14 +#define HCI_SETUP_ESCO_PKT_TYPES_OFF 15 + + +HCI_API extern BOOLEAN btsnd_hcic_accept_esco_conn (BD_ADDR bd_addr, + UINT32 tx_bw, UINT32 rx_bw, + UINT16 max_latency, + UINT16 content_fmt, + UINT8 retrans_effort, + UINT16 packet_types); +#define HCIC_PARAM_SIZE_ACCEPT_ESCO 21 + +#define HCI_ACCEPT_ESCO_BDADDR_OFF 0 +#define HCI_ACCEPT_ESCO_TX_BW_OFF 6 +#define HCI_ACCEPT_ESCO_RX_BW_OFF 10 +#define HCI_ACCEPT_ESCO_MAX_LAT_OFF 14 +#define HCI_ACCEPT_ESCO_VOICE_OFF 16 +#define HCI_ACCEPT_ESCO_RETRAN_EFF_OFF 18 +#define HCI_ACCEPT_ESCO_PKT_TYPES_OFF 19 + + +HCI_API extern BOOLEAN btsnd_hcic_reject_esco_conn (BD_ADDR bd_addr, UINT8 reason); +#define HCIC_PARAM_SIZE_REJECT_ESCO 7 + +#define HCI_REJECT_ESCO_BDADDR_OFF 0 +#define HCI_REJECT_ESCO_REASON_OFF 6 + +/* Hold Mode */ +HCI_API extern BOOLEAN btsnd_hcic_hold_mode(UINT16 handle, UINT16 max_hold_period, + UINT16 min_hold_period); + +#define HCIC_PARAM_SIZE_HOLD_MODE 6 + +#define HCI_HOLD_MODE_HANDLE_OFF 0 +#define HCI_HOLD_MODE_MAX_PER_OFF 2 +#define HCI_HOLD_MODE_MIN_PER_OFF 4 + /* Hold Mode */ + + /* Sniff Mode */ +HCI_API extern BOOLEAN btsnd_hcic_sniff_mode(UINT16 handle, + UINT16 max_sniff_period, + UINT16 min_sniff_period, + UINT16 sniff_attempt, + UINT16 sniff_timeout); + +#define HCIC_PARAM_SIZE_SNIFF_MODE 10 + + +#define HCI_SNIFF_MODE_HANDLE_OFF 0 +#define HCI_SNIFF_MODE_MAX_PER_OFF 2 +#define HCI_SNIFF_MODE_MIN_PER_OFF 4 +#define HCI_SNIFF_MODE_ATTEMPT_OFF 6 +#define HCI_SNIFF_MODE_TIMEOUT_OFF 8 + /* Sniff Mode */ + +HCI_API extern BOOLEAN btsnd_hcic_exit_sniff_mode(UINT16 handle); /* Exit Sniff Mode */ + + /* Park Mode */ +HCI_API extern BOOLEAN btsnd_hcic_park_mode (UINT16 handle, + UINT16 beacon_max_interval, + UINT16 beacon_min_interval); + +#define HCIC_PARAM_SIZE_PARK_MODE 6 + +#define HCI_PARK_MODE_HANDLE_OFF 0 +#define HCI_PARK_MODE_MAX_PER_OFF 2 +#define HCI_PARK_MODE_MIN_PER_OFF 4 + /* Park Mode */ + +HCI_API extern BOOLEAN btsnd_hcic_exit_park_mode(UINT16 handle); /* Exit Park Mode */ + + /* QoS Setup */ +HCI_API extern BOOLEAN btsnd_hcic_qos_setup (UINT16 handle, UINT8 flags, + UINT8 service_type, + UINT32 token_rate, UINT32 peak, + UINT32 latency, UINT32 delay_var); + +#define HCIC_PARAM_SIZE_QOS_SETUP 20 + +#define HCI_QOS_HANDLE_OFF 0 +#define HCI_QOS_FLAGS_OFF 2 +#define HCI_QOS_SERVICE_TYPE_OFF 3 +#define HCI_QOS_TOKEN_RATE_OFF 4 +#define HCI_QOS_PEAK_BANDWIDTH_OFF 8 +#define HCI_QOS_LATENCY_OFF 12 +#define HCI_QOS_DELAY_VAR_OFF 16 + /* QoS Setup */ + +HCI_API extern BOOLEAN btsnd_hcic_role_discovery (UINT16 handle); /* Role Discovery */ + + /* Switch Role Request */ +HCI_API extern BOOLEAN btsnd_hcic_switch_role (BD_ADDR bd_addr, UINT8 role); + +#define HCIC_PARAM_SIZE_SWITCH_ROLE 7 + +#define HCI_SWITCH_BD_ADDR_OFF 0 +#define HCI_SWITCH_ROLE_OFF 6 + /* Switch Role Request */ + +HCI_API extern BOOLEAN btsnd_hcic_read_policy_set(UINT16 handle); /* Read Policy Settings */ + + /* Write Policy Settings */ +HCI_API extern BOOLEAN btsnd_hcic_write_policy_set(UINT16 handle, UINT16 settings); + +#define HCIC_PARAM_SIZE_WRITE_POLICY_SET 4 + +#define HCI_WRITE_POLICY_HANDLE_OFF 0 +#define HCI_WRITE_POLICY_SETTINGS_OFF 2 + /* Write Policy Settings */ + + /* Read Default Policy Settings */ +HCI_API extern BOOLEAN btsnd_hcic_read_def_policy_set(void); + +#define HCIC_PARAM_SIZE_READ_DEF_POLICY_SET 0 + /* Read Default Policy Settings */ + + /* Write Default Policy Settings */ +HCI_API extern BOOLEAN btsnd_hcic_write_def_policy_set(UINT16 settings); + +#define HCIC_PARAM_SIZE_WRITE_DEF_POLICY_SET 2 + +#define HCI_WRITE_DEF_POLICY_SETTINGS_OFF 0 + /* Write Default Policy Settings */ + + /* Flow Specification */ +HCI_API extern BOOLEAN btsnd_hcic_flow_specification(UINT16 handle, UINT8 flags, + UINT8 flow_direct, + UINT8 service_type, + UINT32 token_rate, + UINT32 token_bucket_size, + UINT32 peak, UINT32 latency); + +#define HCIC_PARAM_SIZE_FLOW_SPEC 21 + +#define HCI_FLOW_SPEC_HANDLE_OFF 0 +#define HCI_FLOW_SPEC_FLAGS_OFF 2 +#define HCI_FLOW_SPEC_FLOW_DIRECT_OFF 3 +#define HCI_FLOW_SPEC_SERVICE_TYPE_OFF 4 +#define HCI_FLOW_SPEC_TOKEN_RATE_OFF 5 +#define HCI_FLOW_SPEC_TOKEN_BUCKET_SIZE_OFF 9 +#define HCI_FLOW_SPEC_PEAK_BANDWIDTH_OFF 13 +#define HCI_FLOW_SPEC_LATENCY_OFF 17 + /* Flow Specification */ + +/****************************************** +** Lisbon Features +*******************************************/ +#if BTM_SSR_INCLUDED == TRUE + /* Sniff Subrating */ +HCI_API extern BOOLEAN btsnd_hcic_sniff_sub_rate(UINT16 handle, UINT16 max_lat, + UINT16 min_remote_lat, + UINT16 min_local_lat); + +#define HCIC_PARAM_SIZE_SNIFF_SUB_RATE 8 + +#define HCI_SNIFF_SUB_RATE_HANDLE_OFF 0 +#define HCI_SNIFF_SUB_RATE_MAX_LAT_OFF 2 +#define HCI_SNIFF_SUB_RATE_MIN_REM_LAT_OFF 4 +#define HCI_SNIFF_SUB_RATE_MIN_LOC_LAT_OFF 6 + /* Sniff Subrating */ + +#else /* BTM_SSR_INCLUDED == FALSE */ + +#define btsnd_hcic_sniff_sub_rate(handle, max_lat, min_remote_lat, min_local_lat) FALSE + +#endif /* BTM_SSR_INCLUDED */ + + /* Extended Inquiry Response */ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) +HCI_API extern void btsnd_hcic_write_ext_inquiry_response(void *buffer, UINT8 fec_req); + +#define HCIC_PARAM_SIZE_EXT_INQ_RESP 241 + +#define HCIC_EXT_INQ_RESP_FEC_OFF 0 +#define HCIC_EXT_INQ_RESP_RESPONSE 1 + +HCI_API extern BOOLEAN btsnd_hcic_read_ext_inquiry_response(void); /* Read Extended Inquiry Response */ +#else +#define btsnd_hcic_write_ext_inquiry_response(buffer, fec_req) +#define btsnd_hcic_read_ext_inquiry_response() FALSE +#endif + /* Write Simple Pairing Mode */ +/**** Simple Pairing Commands ****/ +HCI_API extern BOOLEAN btsnd_hcic_write_simple_pairing_mode(UINT8 mode); + +#define HCIC_PARAM_SIZE_W_SIMP_PAIR 1 + +#define HCIC_WRITE_SP_MODE_OFF 0 + + +HCI_API extern BOOLEAN btsnd_hcic_read_simple_pairing_mode (void); + +#define HCIC_PARAM_SIZE_R_SIMP_PAIR 0 + + /* Write Simple Pairing Debug Mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_simp_pair_debug_mode(UINT8 debug_mode); + +#define HCIC_PARAM_SIZE_SIMP_PAIR_DBUG 1 + +#define HCIC_WRITE_SP_DBUG_MODE_OFF 0 + + /* IO Capabilities Response */ +HCI_API extern BOOLEAN btsnd_hcic_io_cap_req_reply (BD_ADDR bd_addr, UINT8 capability, + UINT8 oob_present, UINT8 auth_req); + +#define HCIC_PARAM_SIZE_IO_CAP_RESP 9 + +#define HCI_IO_CAP_BD_ADDR_OFF 0 +#define HCI_IO_CAPABILITY_OFF 6 +#define HCI_IO_CAP_OOB_DATA_OFF 7 +#define HCI_IO_CAP_AUTH_REQ_OFF 8 + + /* IO Capabilities Req Neg Reply */ +HCI_API extern BOOLEAN btsnd_hcic_io_cap_req_neg_reply (BD_ADDR bd_addr, UINT8 err_code); + +#define HCIC_PARAM_SIZE_IO_CAP_NEG_REPLY 7 + +#define HCI_IO_CAP_NR_BD_ADDR_OFF 0 +#define HCI_IO_CAP_NR_ERR_CODE 6 + + /* Read Local OOB Data */ +HCI_API extern BOOLEAN btsnd_hcic_read_local_oob_data (void); + +#define HCIC_PARAM_SIZE_R_LOCAL_OOB 0 + + +HCI_API extern BOOLEAN btsnd_hcic_user_conf_reply (BD_ADDR bd_addr, BOOLEAN is_yes); + +#define HCIC_PARAM_SIZE_UCONF_REPLY 6 + +#define HCI_USER_CONF_BD_ADDR_OFF 0 + + +HCI_API extern BOOLEAN btsnd_hcic_user_passkey_reply (BD_ADDR bd_addr, UINT32 value); + +#define HCIC_PARAM_SIZE_U_PKEY_REPLY 10 + +#define HCI_USER_PASSKEY_BD_ADDR_OFF 0 +#define HCI_USER_PASSKEY_VALUE_OFF 6 + + +HCI_API extern BOOLEAN btsnd_hcic_user_passkey_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_U_PKEY_NEG_REPLY 6 + +#define HCI_USER_PASSKEY_NEG_BD_ADDR_OFF 0 + + /* Remote OOB Data Request Reply */ +HCI_API extern BOOLEAN btsnd_hcic_rem_oob_reply (BD_ADDR bd_addr, UINT8 *p_c, + UINT8 *p_r); + +#define HCIC_PARAM_SIZE_REM_OOB_REPLY 38 + +#define HCI_REM_OOB_DATA_BD_ADDR_OFF 0 +#define HCI_REM_OOB_DATA_C_OFF 6 +#define HCI_REM_OOB_DATA_R_OFF 22 + + /* Remote OOB Data Request Negative Reply */ +HCI_API extern BOOLEAN btsnd_hcic_rem_oob_neg_reply (BD_ADDR bd_addr); + +#define HCIC_PARAM_SIZE_REM_OOB_NEG_REPLY 6 + +#define HCI_REM_OOB_DATA_NEG_BD_ADDR_OFF 0 + + /* Read Tx Power Level */ +HCI_API extern BOOLEAN btsnd_hcic_read_inq_tx_power (void); + +#define HCIC_PARAM_SIZE_R_TX_POWER 0 + + /* Write Tx Power Level */ +HCI_API extern BOOLEAN btsnd_hcic_write_inq_tx_power (INT8 level); + +#define HCIC_PARAM_SIZE_W_TX_POWER 1 + +#define HCIC_WRITE_TX_POWER_LEVEL_OFF 0 + /* Read Default Erroneous Data Reporting */ +HCI_API extern BOOLEAN btsnd_hcic_read_default_erroneous_data_rpt (void); + +#define HCIC_PARAM_SIZE_R_ERR_DATA_RPT 0 + + /* Write Default Erroneous Data Reporting */ +HCI_API extern BOOLEAN btsnd_hcic_write_default_erroneous_data_rpt (UINT8 level); + +#define HCIC_PARAM_SIZE_W_ERR_DATA_RPT 1 + +#define HCIC_WRITE_ERR_DATA_RPT_OFF 0 + + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE +HCI_API extern BOOLEAN btsnd_hcic_enhanced_flush (UINT16 handle, UINT8 packet_type); + +#define HCIC_PARAM_SIZE_ENHANCED_FLUSH 3 +#endif + + +HCI_API extern BOOLEAN btsnd_hcic_send_keypress_notif (BD_ADDR bd_addr, UINT8 notif); + +#define HCIC_PARAM_SIZE_SEND_KEYPRESS_NOTIF 7 + +#define HCI_SEND_KEYPRESS_NOTIF_BD_ADDR_OFF 0 +#define HCI_SEND_KEYPRESS_NOTIF_NOTIF_OFF 6 + + +HCI_API extern BOOLEAN btsnd_hcic_refresh_encryption_key(UINT16 handle); /* Refresh Encryption Key */ + +/**** end of Simple Pairing Commands ****/ + + +HCI_API extern BOOLEAN btsnd_hcic_set_event_mask(UINT8 local_controller_id, BT_EVENT_MASK evt_mask); + +#define HCIC_PARAM_SIZE_SET_EVENT_MASK 8 +#define HCI_EVENT_MASK_MASK_OFF 0 + /* Set Event Mask */ + + /* Reset */ +HCI_API extern BOOLEAN btsnd_hcic_set_event_mask_page_2 (UINT8 local_controller_id, + BT_EVENT_MASK event_mask); + +#define HCIC_PARAM_SIZE_SET_EVENT_MASK_PAGE_2 8 +#define HCI_EVENT_MASK_MASK_OFF 0 + /* Set Event Mask Page 2 */ + + /* Reset */ +HCI_API extern BOOLEAN btsnd_hcic_reset(UINT8 local_controller_id); + +#define HCIC_PARAM_SIZE_RESET 0 + /* Reset */ + + /* Store Current Settings */ +#define MAX_FILT_COND (sizeof (BD_ADDR) + 1) + +HCI_API extern BOOLEAN btsnd_hcic_set_event_filter(UINT8 filt_type, + UINT8 filt_cond_type, + UINT8 *filt_cond, + UINT8 filt_cond_len); + +#define HCIC_PARAM_SIZE_SET_EVT_FILTER 9 + +#define HCI_FILT_COND_FILT_TYPE_OFF 0 +#define HCI_FILT_COND_COND_TYPE_OFF 1 +#define HCI_FILT_COND_FILT_OFF 2 + /* Set Event Filter */ + +HCI_API extern BOOLEAN btsnd_hcic_flush(UINT8 local_controller_id, UINT16 handle); /* Flush */ + + /* Create New Unit Type */ +HCI_API extern BOOLEAN btsnd_hcic_new_unit_key(void); + +#define HCIC_PARAM_SIZE_NEW_UNIT_KEY 0 + /* Create New Unit Type */ + + /* Read Stored Key */ +HCI_API extern BOOLEAN btsnd_hcic_read_stored_key (BD_ADDR bd_addr, + BOOLEAN read_all_flag); + +#define HCIC_PARAM_SIZE_READ_STORED_KEY 7 + +#define HCI_READ_KEY_BD_ADDR_OFF 0 +#define HCI_READ_KEY_ALL_FLAG_OFF 6 + /* Read Stored Key */ + +#define MAX_WRITE_KEYS 10 + /* Write Stored Key */ +HCI_API extern BOOLEAN btsnd_hcic_write_stored_key (UINT8 num_keys, BD_ADDR *bd_addr, + LINK_KEY *link_key); + +#define HCIC_PARAM_SIZE_WRITE_STORED_KEY sizeof(btmsg_hcic_write_stored_key_t) + +#define HCI_WRITE_KEY_NUM_KEYS_OFF 0 +#define HCI_WRITE_KEY_BD_ADDR_OFF 1 +#define HCI_WRITE_KEY_KEY_OFF 7 +/* only 0x0b keys cab be sent in one HCI command */ +#define HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD 0x0b + /* Write Stored Key */ + + /* Delete Stored Key */ +HCI_API extern BOOLEAN btsnd_hcic_delete_stored_key (BD_ADDR bd_addr, BOOLEAN delete_all_flag); + +#define HCIC_PARAM_SIZE_DELETE_STORED_KEY 7 + +#define HCI_DELETE_KEY_BD_ADDR_OFF 0 +#define HCI_DELETE_KEY_ALL_FLAG_OFF 6 + /* Delete Stored Key */ + + /* Change Local Name */ +HCI_API extern BOOLEAN btsnd_hcic_change_name(BD_NAME name); + +#define HCIC_PARAM_SIZE_CHANGE_NAME BD_NAME_LEN + +#define HCI_CHANGE_NAME_NAME_OFF 0 + /* Change Local Name */ + + +#define HCIC_PARAM_SIZE_READ_CMD 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM1 1 + +#define HCIC_WRITE_PARAM1_PARAM_OFF 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM2 2 + +#define HCIC_WRITE_PARAM2_PARAM_OFF 0 + +#define HCIC_PARAM_SIZE_WRITE_PARAM3 3 + +#define HCIC_WRITE_PARAM3_PARAM_OFF 0 + +#define HCIC_PARAM_SIZE_SET_AFH_CHANNELS 10 + +HCI_API extern BOOLEAN btsnd_hcic_read_pin_type(void); /* Read PIN Type */ +HCI_API extern BOOLEAN btsnd_hcic_write_pin_type(UINT8 type); /* Write PIN Type */ +HCI_API extern BOOLEAN btsnd_hcic_read_auto_accept(void); /* Read Auto Accept */ +HCI_API extern BOOLEAN btsnd_hcic_write_auto_accept(UINT8 flag); /* Write Auto Accept */ +HCI_API extern BOOLEAN btsnd_hcic_read_name (void); /* Read Local Name */ +HCI_API extern BOOLEAN btsnd_hcic_read_conn_acc_tout(UINT8 local_controller_id); /* Read Connection Accept Timout */ +HCI_API extern BOOLEAN btsnd_hcic_write_conn_acc_tout(UINT8 local_controller_id, UINT16 tout); /* Write Connection Accept Timout */ +HCI_API extern BOOLEAN btsnd_hcic_read_page_tout(void); /* Read Page Timout */ +HCI_API extern BOOLEAN btsnd_hcic_write_page_tout(UINT16 timeout); /* Write Page Timout */ +HCI_API extern BOOLEAN btsnd_hcic_read_scan_enable(void); /* Read Scan Enable */ +HCI_API extern BOOLEAN btsnd_hcic_write_scan_enable(UINT8 flag); /* Write Scan Enable */ +HCI_API extern BOOLEAN btsnd_hcic_read_pagescan_cfg(void); /* Read Page Scan Activity */ + +HCI_API extern BOOLEAN btsnd_hcic_write_pagescan_cfg(UINT16 interval, + UINT16 window); /* Write Page Scan Activity */ + +#define HCIC_PARAM_SIZE_WRITE_PAGESCAN_CFG 4 + +#define HCI_SCAN_CFG_INTERVAL_OFF 0 +#define HCI_SCAN_CFG_WINDOW_OFF 2 + /* Write Page Scan Activity */ + +HCI_API extern BOOLEAN btsnd_hcic_read_inqscan_cfg(void); /* Read Inquiry Scan Activity */ + + /* Write Inquiry Scan Activity */ +HCI_API extern BOOLEAN btsnd_hcic_write_inqscan_cfg(UINT16 interval, UINT16 window); + +#define HCIC_PARAM_SIZE_WRITE_INQSCAN_CFG 4 + +#define HCI_SCAN_CFG_INTERVAL_OFF 0 +#define HCI_SCAN_CFG_WINDOW_OFF 2 + /* Write Inquiry Scan Activity */ + +HCI_API extern BOOLEAN btsnd_hcic_read_auth_enable(void); /* Read Authentication Enable */ +HCI_API extern BOOLEAN btsnd_hcic_write_auth_enable(UINT8 flag); /* Write Authentication Enable */ +HCI_API extern BOOLEAN btsnd_hcic_read_encr_mode (void); /* Read encryption mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_encr_mode (UINT8 mode); /* Write encryption mode */ +HCI_API extern BOOLEAN btsnd_hcic_read_dev_class(void); /* Read Class of Device */ +HCI_API extern BOOLEAN btsnd_hcic_write_dev_class(DEV_CLASS dev); /* Write Class of Device */ +HCI_API extern BOOLEAN btsnd_hcic_read_voice_settings(void); /* Read Voice Settings */ +HCI_API extern BOOLEAN btsnd_hcic_write_voice_settings(UINT16 flags); /* Write Voice Settings */ + +/* Host Controller to Host flow control */ +#define HCI_HOST_FLOW_CTRL_OFF 0 +#define HCI_HOST_FLOW_CTRL_ACL_ON 1 +#define HCI_HOST_FLOW_CTRL_SCO_ON 2 +#define HCI_HOST_FLOW_CTRL_BOTH_ON 3 + +HCI_API extern BOOLEAN btsnd_hcic_set_host_flow_ctrl (UINT8 value); /* Enable/disable flow control toward host */ + + +HCI_API extern BOOLEAN btsnd_hcic_read_auto_flush_tout(UINT16 handle); /* Read Retransmit Timout */ + +HCI_API extern BOOLEAN btsnd_hcic_write_auto_flush_tout(UINT16 handle, + UINT16 timeout); /* Write Retransmit Timout */ + +#define HCIC_PARAM_SIZE_WRITE_AUTO_FLUSH_TOUT 4 + +#define HCI_FLUSH_TOUT_HANDLE_OFF 0 +#define HCI_FLUSH_TOUT_TOUT_OFF 2 + +HCI_API extern BOOLEAN btsnd_hcic_read_num_bcast_xmit(void); /* Read Num Broadcast Retransmits */ +HCI_API extern BOOLEAN btsnd_hcic_write_num_bcast_xmit(UINT8 num); /* Write Num Broadcast Retransmits */ +HCI_API extern BOOLEAN btsnd_hcic_read_hold_mode_act(void); /* Read Hold Mode Activity */ +HCI_API extern BOOLEAN btsnd_hcic_write_hold_mode_act(UINT8 flags); /* Write Hold Mode Activity */ + +HCI_API extern BOOLEAN btsnd_hcic_read_tx_power(UINT16 handle, UINT8 type); /* Read Tx Power */ + +#define HCIC_PARAM_SIZE_READ_TX_POWER 3 + +#define HCI_READ_TX_POWER_HANDLE_OFF 0 +#define HCI_READ_TX_POWER_TYPE_OFF 2 + +/* Read transmit power level parameter */ +#define HCI_READ_CURRENT 0x00 +#define HCI_READ_MAXIMUM 0x01 + +HCI_API extern BOOLEAN btsnd_hcic_read_sco_flow_enable(void); /* Read Authentication Enable */ +HCI_API extern BOOLEAN btsnd_hcic_write_sco_flow_enable(UINT8 flag); /* Write Authentication Enable */ + + /* Set Host Buffer Size */ +HCI_API extern BOOLEAN btsnd_hcic_set_host_buf_size (UINT16 acl_len, + UINT8 sco_len, + UINT16 acl_num, + UINT16 sco_num); + +#define HCIC_PARAM_SIZE_SET_HOST_BUF_SIZE 7 + +#define HCI_HOST_BUF_SIZE_ACL_LEN_OFF 0 +#define HCI_HOST_BUF_SIZE_SCO_LEN_OFF 2 +#define HCI_HOST_BUF_SIZE_ACL_NUM_OFF 3 +#define HCI_HOST_BUF_SIZE_SCO_NUM_OFF 5 + + +HCI_API extern BOOLEAN btsnd_hcic_host_num_xmitted_pkts (UINT8 num_handles, + UINT16 *handle, + UINT16 *num_pkts); /* Set Host Buffer Size */ + +#define HCIC_PARAM_SIZE_NUM_PKTS_DONE_SIZE sizeof(btmsg_hcic_num_pkts_done_t) + +#define MAX_DATA_HANDLES 10 + +#define HCI_PKTS_DONE_NUM_HANDLES_OFF 0 +#define HCI_PKTS_DONE_HANDLE_OFF 1 +#define HCI_PKTS_DONE_NUM_PKTS_OFF 3 + +HCI_API extern BOOLEAN btsnd_hcic_read_link_super_tout(UINT8 local_controller_id, UINT16 handle); /* Read Link Supervision Timeout */ + + /* Write Link Supervision Timeout */ +HCI_API extern BOOLEAN btsnd_hcic_write_link_super_tout(UINT8 local_controller_id, UINT16 handle, UINT16 timeout); + +#define HCIC_PARAM_SIZE_WRITE_LINK_SUPER_TOUT 4 + +#define HCI_LINK_SUPER_TOUT_HANDLE_OFF 0 +#define HCI_LINK_SUPER_TOUT_TOUT_OFF 2 + /* Write Link Supervision Timeout */ + +HCI_API extern BOOLEAN btsnd_hcic_read_max_iac (void); /* Read Num Supported IAC */ +HCI_API extern BOOLEAN btsnd_hcic_read_cur_iac_lap (void); /* Read Current IAC LAP */ + +HCI_API extern BOOLEAN btsnd_hcic_write_cur_iac_lap (UINT8 num_cur_iac, + LAP * const iac_lap); /* Write Current IAC LAP */ + +#define MAX_IAC_LAPS 0x40 + +#define HCI_WRITE_IAC_LAP_NUM_OFF 0 +#define HCI_WRITE_IAC_LAP_LAP_OFF 1 + /* Write Current IAC LAP */ + + /* Read Clock */ +HCI_API extern BOOLEAN btsnd_hcic_read_clock (UINT16 handle, UINT8 which_clock); + +#define HCIC_PARAM_SIZE_READ_CLOCK 3 + +#define HCI_READ_CLOCK_HANDLE_OFF 0 +#define HCI_READ_CLOCK_WHICH_CLOCK 2 + /* Read Clock */ + +#ifdef TESTER_ENABLE + +#define HCIC_PARAM_SIZE_ENTER_TEST_MODE 2 + +#define HCI_ENTER_TEST_HANDLE_OFF 0 + +#define HCIC_PARAM_SIZE_TEST_CNTRL 10 +#define HCI_TEST_CNTRL_HANDLE_OFF 0 +#define HCI_TEST_CNTRL_SCENARIO_OFF 2 +#define HCI_TEST_CNTRL_HOPPINGMODE_OFF 3 +#define HCI_TEST_CNTRL_TX_FREQ_OFF 4 +#define HCI_TEST_CNTRL_RX_FREQ_OFF 5 +#define HCI_TEST_CNTRL_PWR_CNTRL_MODE_OFF 6 +#define HCI_TEST_CNTRL_POLL_PERIOD_OFF 7 +#define HCI_TEST_CNTRL_PKT_TYPE_OFF 8 +#define HCI_TEST_CNTRL_LENGTH_OFF 9 + +#endif + +HCI_API extern BOOLEAN btsnd_hcic_read_page_scan_per (void); /* Read Page Scan Period Mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_page_scan_per (UINT8 mode); /* Write Page Scan Period Mode */ +HCI_API extern BOOLEAN btsnd_hcic_read_page_scan_mode (void); /* Read Page Scan Mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_page_scan_mode (UINT8 mode); /* Write Page Scan Mode */ +HCI_API extern BOOLEAN btsnd_hcic_read_local_ver (UINT8 local_controller_id); /* Read Local Version Info */ +HCI_API extern BOOLEAN btsnd_hcic_read_local_supported_cmds (UINT8 local_controller_id); /* Read Local Supported Commands */ +HCI_API extern BOOLEAN btsnd_hcic_read_local_features (void); /* Read Local Supported Features */ +HCI_API extern BOOLEAN btsnd_hcic_read_buffer_size (void); /* Read Local buffer sizes */ +HCI_API extern BOOLEAN btsnd_hcic_read_country_code (void); /* Read Country Code */ +HCI_API extern BOOLEAN btsnd_hcic_read_bd_addr (void); /* Read Local BD_ADDR */ +HCI_API extern BOOLEAN btsnd_hcic_read_fail_contact_count (UINT8 local_controller_id, UINT16 handle); /* Read Failed Contact Counter */ +HCI_API extern BOOLEAN btsnd_hcic_reset_fail_contact_count (UINT8 local_controller_id, UINT16 handle);/* Reset Failed Contact Counter */ +HCI_API extern BOOLEAN btsnd_hcic_get_link_quality (UINT16 handle); /* Get Link Quality */ +HCI_API extern BOOLEAN btsnd_hcic_read_rssi (UINT16 handle); /* Read RSSI */ +HCI_API extern BOOLEAN btsnd_hcic_read_loopback_mode (void); /* Read Loopback Mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_loopback_mode (UINT8 mode); /* Write Loopback Mode */ +HCI_API extern BOOLEAN btsnd_hcic_enable_test_mode (void); /* Enable Device Under Test Mode */ +HCI_API extern BOOLEAN btsnd_hcic_write_pagescan_type(UINT8 type); /* Write Page Scan Type */ +HCI_API extern BOOLEAN btsnd_hcic_read_pagescan_type(void); /* Read Page Scan Type */ +HCI_API extern BOOLEAN btsnd_hcic_write_inqscan_type(UINT8 type); /* Write Inquiry Scan Type */ +HCI_API extern BOOLEAN btsnd_hcic_read_inqscan_type(void); /* Read Inquiry Scan Type */ +HCI_API extern BOOLEAN btsnd_hcic_write_inquiry_mode(UINT8 type); /* Write Inquiry Mode */ +HCI_API extern BOOLEAN btsnd_hcic_read_inquiry_mode(void); /* Read Inquiry Mode */ +HCI_API extern BOOLEAN btsnd_hcic_set_afh_channels (UINT8 first, UINT8 last); +HCI_API extern BOOLEAN btsnd_hcic_write_afh_channel_assessment_mode (UINT8 mode); +HCI_API extern BOOLEAN btsnd_hcic_set_afh_host_channel_class (UINT8 *p_afhchannelmap); +HCI_API extern BOOLEAN btsnd_hcic_read_afh_channel_assessment_mode(void); +HCI_API extern BOOLEAN btsnd_hcic_read_afh_channel_map (UINT16 handle); +HCI_API extern BOOLEAN btsnd_hcic_nop(void); /* NOP */ + + /* Send HCI Data */ +HCI_API extern void btsnd_hcic_data (BT_HDR *p_buf, UINT16 len, UINT16 handle, UINT8 boundary, UINT8 broadcast); + +#define HCI_DATA_HANDLE_MASK 0x0FFF + +#define HCID_GET_HANDLE_EVENT(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset) + \ + (*((UINT8 *)((p) + 1) + p->offset + 1) << 8))) + +#define HCID_GET_HANDLE(u16) (UINT16)((u16) & HCI_DATA_HANDLE_MASK) + +#define HCI_DATA_EVENT_MASK 3 +#define HCI_DATA_EVENT_OFFSET 12 +#define HCID_GET_EVENT(u16) (UINT8)(((u16) >> HCI_DATA_EVENT_OFFSET) & HCI_DATA_EVENT_MASK) + +#define HCI_DATA_BCAST_MASK 3 +#define HCI_DATA_BCAST_OFFSET 10 +#define HCID_GET_BCAST(u16) (UINT8)(((u16) >> HCI_DATA_BCAST_OFFSET) & HCI_DATA_BCAST_MASK) + +#define HCID_GET_ACL_LEN(p) (UINT16)((*((UINT8 *)((p) + 1) + p->offset + 2) + \ + (*((UINT8 *)((p) + 1) + p->offset + 3) << 8))) + +#define HCID_HEADER_SIZE 4 + /* Send HCI Data */ + +#define HCID_GET_SCO_LEN(p) (*((UINT8 *)((p) + 1) + p->offset + 2)) + +HCI_API extern void btsnd_hcic_vendor_spec_cmd ( + void *buffer, UINT16 opcode, + UINT8 len, UINT8 *p_data, + void *p_cmd_cplt_cback); + + +/********************************************************************************* +** ** +** H C I E V E N T S ** +** ** +*********************************************************************************/ + +/* Inquiry Complete Event */ +HCI_API extern void btsnd_hcie_inq_comp(void *buffer, UINT8 status); + +#define HCIE_PARAM_SIZE_INQ_COMP 1 + +/* Inquiry Response Event */ +HCI_API extern void btsnd_hcie_inq_res(void *buffer, UINT8 num_resp, UINT8 **bd_addr, + UINT8 *page_scan_rep_mode, UINT8 *page_scan_per_mode, + UINT8 *page_scan_mode, UINT8 **dev_class, + UINT16 *clock_offset); + +/* Connection Complete Event */ +HCI_API extern void btsnd_hcie_connection_comp(void *buffer, UINT8 status, UINT16 handle, + BD_ADDR bd_addr, UINT8 link_type, UINT8 encr_mode); + +#define HCIE_PARAM_SIZE_CONNECTION_COMP 11 + + +#define HCI_LINK_TYPE_SCO 0x00 +#define HCI_LINK_TYPE_ACL 0x01 + +#define HCI_ENCRYPT_MODE_DISABLED 0x00 +#define HCI_ENCRYPT_MODE_POINT_TO_POINT 0x01 +#define HCI_ENCRYPT_MODE_ALL 0x02 + + +/* Connection Request Event */ +HCI_API extern void btsnd_hcie_connection_req(void *buffer, BD_ADDR bd_addr, DEV_CLASS dev_class, UINT8 link_type); + +#define HCIE_PARAM_SIZE_CONNECTION_REQ 10 + +#define HCI_LINK_TYPE_SCO 0x00 +#define HCI_LINK_TYPE_ACL 0x01 + + +/* Disonnection Complete Event */ +HCI_API extern void btsnd_hcie_disc_comp(void *buffer, UINT8 status, UINT16 handle, UINT8 reason); + +#define HCIE_PARAM_SIZE_DISC_COMP 4 + + +/* Authentication Complete Event */ +HCI_API extern void btsnd_hcie_auth_comp (void *buffer, UINT8 status, UINT16 handle); + +#define HCIE_PARAM_SIZE_AUTH_COMP 3 + + +/* Remote Name Request Complete Event */ +HCI_API extern void btsnd_hcie_rmt_name_req_comp(void *buffer, UINT8 status, BD_ADDR bd_addr, BD_NAME name); + +#define HCIE_PARAM_SIZE_RMT_NAME_REQ_COMP (1 + BD_ADDR_LEN + BD_NAME_LEN) + + +/* Encryption Change Event */ +HCI_API extern void btsnd_hcie_encryption_change (void *buffer, UINT8 status, UINT16 handle, BOOLEAN enable); + +#define HCIE_PARAM_SIZE_ENCR_CHANGE 4 + + +/* Connection Link Key Change Event */ +HCI_API extern void btsnd_hcie_conn_link_key_change (void *buffer, UINT8 status, UINT16 handle); + +#define HCIE_PARAM_SIZE_LINK_KEY_CHANGE 3 + + +/* Encryption Key Refresh Complete Event */ +HCI_API extern void btsnd_hcie_encrypt_key_refresh (void *buffer, UINT8 status, UINT16 handle); + +#define HCIE_PARAM_SIZE_ENCRYPT_KEY_REFRESH 3 + + +/* Master Link Key Complete Event */ +HCI_API extern void btsnd_hcie_master_link_key (void *buffer, UINT8 status, UINT16 handle, UINT8 flag); + +#define HCIE_PARAM_SIZE_MASTER_LINK_KEY 4 + + +/* Read Remote Supported Features Complete Event */ +HCI_API extern void btsnd_hcie_read_rmt_features (void *buffer, UINT8 status, UINT16 handle, UINT8 *features); + +#define LMP_FEATURES_SIZE 8 +#define HCIE_PARAM_SIZE_READ_RMT_FEATURES 11 + + +/* Read Remote Extended Features Complete Event */ +HCI_API extern void btsnd_hcie_read_rmt_ext_features (void *buffer, UINT8 status, UINT16 handle, UINT8 page_num, + UINT8 max_page_num, UINT8 *features); + +#define EXT_LMP_FEATURES_SIZE 8 +#define HCIE_PARAM_SIZE_READ_RMT_EXT_FEATURES 13 + + +/* Read Remote Version Complete Event */ +HCI_API extern void btsnd_hcie_read_rmt_version (void *buffer, UINT8 status, UINT16 handle, UINT8 version, + UINT16 comp_name, UINT16 sub_version); + +#define HCIE_PARAM_SIZE_READ_RMT_VERSION 8 + + +/* QOS setup complete */ +HCI_API extern void btsnd_hcie_qos_setup_compl (void *buffer, UINT8 status, UINT16 handle, UINT8 flags, + UINT8 service_type, UINT32 token_rate, UINT32 peak, + UINT32 latency, UINT32 delay_var); + +#define HCIE_PARAM_SIZE_QOS_SETUP_COMP 21 + + +/* Flow Specification complete */ +HCI_API extern void btsnd_hcie_flow_spec_compl (void *buffer, UINT8 status, UINT16 handle, UINT8 flags, + UINT8 flow_direction, UINT8 service_type, UINT32 token_rate, UINT32 token_bucket_size, + UINT32 peak, UINT32 latency); + +#define HCIE_PARAM_SIZE_FLOW_SPEC_COMP 22 + + +/* Command Complete Event */ +HCI_API extern void btsnd_hcie_cmd_comp(void *buffer, UINT8 max_host_cmds, UINT16 opcode, UINT8 status); + +#define HCIE_PARAM_SIZE_CMD_COMP 4 + + +/* Command Complete with pre-filled in parameters */ +HCI_API extern void btsnd_hcie_cmd_comp_params (void *buffer, UINT8 max_host_cmds, UINT16 cmd_opcode, UINT8 status); + +#define HCI_CMD_COMPL_PARAM_OFFSET 4 + + +/* Command Complete Event with 1-byte param */ +HCI_API extern void btsnd_hcie_cmd_comp_param1(void *buffer, UINT8 max_host_cmds, UINT16 opcode, + UINT8 status, UINT8 param1); + +#define HCIE_PARAM_SIZE_CMD_COMP_PARAM1 5 + +/* Command Complete Event with 2-byte param */ +HCI_API extern void btsnd_hcie_cmd_comp_param2(void *buffer, UINT8 max_host_cmds, UINT16 opcode, + UINT8 status, UINT16 param2); + +#define HCIE_PARAM_SIZE_CMD_COMP_PARAM2 6 + + +/* Command Complete Event with BD-addr as param */ +HCI_API extern void btsnd_hcie_cmd_comp_bd_addr(void *buffer, UINT8 max_host_cmds, UINT16 opcode, + UINT8 status, BD_ADDR bd_addr); + +#define HCIE_PARAM_SIZE_CMD_COMP_BD_ADDR 10 + + +/* Command Pending Event */ +HCI_API extern void btsnd_hcie_cmd_status (void *buffer, UINT8 status, UINT8 max_host_cmds, UINT16 opcode); + +#define HCIE_PARAM_SIZE_CMD_STATUS 4 + + +/* HW failure Event */ +HCI_API extern void btsnd_hcie_hw_failure (void *buffer, UINT8 code); + +#define HCIE_PARAM_SIZE_HW_FAILURE 1 + + +/* Flush Occured Event */ +HCI_API extern void btsnd_hcie_flush_occured (void *buffer, UINT16 handle); + +#define HCIE_PARAM_SIZE_FLUSH_OCCURED 2 + + +/* Role Changed Event */ +HCI_API extern void btsnd_hcie_role_change (void *buffer, UINT8 status, BD_ADDR bd_addr, UINT8 role); + +#define HCIE_PARAM_SIZE_ROLE_CHANGE 8 + + +/* Ready for Data Packets Event */ +HCI_API extern void btsnd_hcie_num_compl_pkts (void *buffer, UINT8 num_handles, UINT16 *p_handle, UINT16 *num_pkts); + +#define MAX_DATA_HANDLES 10 + + +/* Mode Change Event */ +HCI_API extern void btsnd_hcie_mode_change (void *buffer, UINT8 status, UINT16 handle, + UINT8 mode, UINT16 interval); + +#define HCIE_PARAM_SIZE_MODE_CHANGE 6 +#define MAX_DATA_HANDLES 10 + + + +/* Return Link Keys Event */ +HCI_API extern void btsnd_hcie_return_link_keys (void *buffer, UINT8 num_keys, BD_ADDR *bd_addr, LINK_KEY *link_key); + +/* This should not be more than 0x0b */ +#define MAX_LINK_KEYS 10 + + + +/* PIN Code Request Event */ +HCI_API extern void btsnd_hcie_pin_code_req (void *buffer, BD_ADDR bd_addr); + +#define HCIE_PARAM_SIZE_PIN_CODE_REQ 6 + + + +/* Link Key Request Event */ +HCI_API extern void btsnd_hcie_link_key_req (void *buffer, BD_ADDR bd_addr); + +#define HCIE_PARAM_SIZE_LINK_KEY_REQ 6 + + + +/* Link Key Notification Event */ +HCI_API extern void btsnd_hcie_link_key_notify (void *buffer, BD_ADDR bd_addr, LINK_KEY link_key, UINT8 key_type); + +#define HCIE_PARAM_SIZE_LINK_KEY_NOTIFY 23 + + + +/* Loopback Command Event */ +HCI_API extern void btsnd_hcie_loopback_command (void *buffer, UINT8 data_len, UINT8 *data); + +#define HCIE_PARAM_SIZE_LOOPBACK_COMMAND sizeof(btmsg_hcie_loopback_cmd_t) + + + +/* Data Buffer Overflow Event */ +HCI_API extern void btsnd_hcie_data_buf_overflow (void *buffer, UINT8 link_type); + +#define HCIE_PARAM_SIZE_DATA_BUF_OVERFLOW 1 + + + +/* Max Slots Change Event */ +HCI_API extern void btsnd_hcie_max_slots_change(void *buffer, UINT16 handle, UINT8 max_slots); + +#define HCIE_PARAM_SIZE_MAX_SLOTS_CHANGE 3 + + +/* Read Clock Offset Complet Event */ +HCI_API extern void btsnd_hcie_read_clock_off_comp(void *buffer, UINT8 status, UINT16 handle, + UINT16 clock_offset); + +#define HCIE_PARAM_SIZE_READ_CLOCK_OFF_COMP 5 + + + +/* Connection Packet Type Change Event */ +HCI_API extern void btsnd_hcie_pkt_type_change (void *buffer, UINT8 status, UINT16 handle, UINT16 pkt_type); + +#define HCIE_PARAM_SIZE_PKT_TYPE_CHANGE 5 + + + +/* QOS violation Event */ +HCI_API extern void btsnd_hcie_qos_violation (void *buffer, UINT16 handle); + +#define HCIE_PARAM_SIZE_QOS_VIOLATION 2 + + + +/* Page Scan Mode Change Event */ +HCI_API extern void btsnd_hcie_pagescan_mode_chng (void *buffer, BD_ADDR bd_addr, UINT8 mode); + +#define HCIE_PARAM_SIZE_PAGE_SCAN_MODE_CHNG 7 + + +/* Page Scan Repetition Mode Change Event */ +HCI_API extern void btsnd_hcie_pagescan_rep_mode_chng (void *buffer, BD_ADDR bd_addr, UINT8 mode); + +#define HCIE_PARAM_SIZE_PAGE_SCAN_REP_MODE_CHNG 7 + + +/* Sniff Sub Rate Event */ +HCI_API extern void btsnd_hcie_sniff_sub_rate(void *buffer, UINT8 status, UINT16 handle, UINT16 max_tx_lat, UINT16 max_rx_lat, + UINT16 min_remote_timeout, UINT16 min_local_timeout); + +#define HCIE_PARAM_SIZE_SNIFF_SUB_RATE 11 + + + +/* Extended Inquiry Result Event */ +HCI_API extern void btsnd_hcie_ext_inquiry_result(void *buffer, UINT8 num_resp, UINT8 **bd_addr, + UINT8 *page_scan_rep_mode, UINT8 *reserved, + UINT8 **dev_class, UINT16 *clock_offset, UINT8 *rssi, UINT8 *p_data); + + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************** +** BLE Commands +** Note: "local_controller_id" is for transport, not counted in HCI message size +*********************************************************************************/ +#define HCIC_PARAM_SIZE_SET_USED_FEAT_CMD 8 +#define HCIC_PARAM_SIZE_WRITE_RANDOM_ADDR_CMD 6 +#define HCIC_PARAM_SIZE_BLE_WRITE_ADV_PARAMS 15 +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_RSP 31 +#define HCIC_PARAM_SIZE_WRITE_ADV_ENABLE 1 +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_PARAM 7 +#define HCIC_PARAM_SIZE_BLE_WRITE_SCAN_ENABLE 2 +#define HCIC_PARAM_SIZE_BLE_CREATE_LL_CONN 25 +#define HCIC_PARAM_SIZE_BLE_CREATE_CONN_CANCEL 0 +#define HCIC_PARAM_SIZE_CLEAR_WHITE_LIST 0 +#define HCIC_PARAM_SIZE_ADD_WHITE_LIST 7 +#define HCIC_PARAM_SIZE_REMOVE_WHITE_LIST 7 +#define HCIC_PARAM_SIZE_BLE_UPD_LL_CONN_PARAMS 14 +#define HCIC_PARAM_SIZE_SET_HOST_CHNL_CLASS 5 +#define HCIC_PARAM_SIZE_READ_CHNL_MAP 2 +#define HCIC_PARAM_SIZE_BLE_READ_REMOTE_FEAT 2 +#define HCIC_PARAM_SIZE_BLE_ENCRYPT 32 +#define HCIC_PARAM_SIZE_BLE_RAND 0 + +#define HCIC_BLE_RAND_DI_SIZE 8 +#define HCIC_BLE_ENCRYT_KEY_SIZE 16 +#define HCIC_PARAM_SIZE_BLE_START_ENC (4 + HCIC_BLE_RAND_DI_SIZE + HCIC_BLE_ENCRYT_KEY_SIZE) +#define HCIC_PARAM_SIZE_LTK_REQ_REPLY (2 + HCIC_BLE_ENCRYT_KEY_SIZE) +#define HCIC_PARAM_SIZE_LTK_REQ_NEG_REPLY 2 +#define HCIC_BLE_CHNL_MAP_SIZE 5 +#define HCIC_PARAM_SIZE_BLE_WRITE_ADV_DATA 31 + +/* ULP HCI command */ +HCI_API extern BOOLEAN btsnd_hcic_ble_reset(void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_evt_mask (BT_EVENT_MASK event_mask); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_buffer_size (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_local_spt_feat (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_local_used_feat (UINT8 feat_set[8]); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_random_addr (BD_ADDR random_addr); + +HCI_API extern BOOLEAN btsnd_hcic_ble_write_adv_params (UINT16 adv_int_min, UINT16 adv_int_max, + UINT8 adv_type, UINT8 addr_type_own, + UINT8 addr_type_dir, BD_ADDR direct_bda, + UINT8 channel_map, UINT8 adv_filter_policy); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_adv_chnl_tx_power (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_adv_data (UINT8 data_len, UINT8 *p_data); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_scan_rsp_data (UINT8 data_len, UINT8 *p_scan_rsp); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_adv_enable (UINT8 adv_enable); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_scan_params (UINT8 scan_type, + UINT16 scan_int, UINT16 scan_win, + UINT8 addr_type, UINT8 scan_filter_policy); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_scan_enable (UINT8 scan_enable, UINT8 duplicate); + +HCI_API extern BOOLEAN btsnd_hcic_ble_create_ll_conn (UINT16 scan_int, UINT16 scan_win, + UINT8 init_filter_policy, UINT8 addr_type_peer, BD_ADDR bda_peer, UINT8 addr_type_own, + UINT16 conn_int_min, UINT16 conn_int_max, UINT16 conn_latency, UINT16 conn_timeout, + UINT16 min_ce_len, UINT16 max_ce_len); + +HCI_API extern BOOLEAN btsnd_hcic_ble_create_conn_cancel (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_white_list_size (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_clear_white_list (void); + +HCI_API extern BOOLEAN btsnd_hcic_ble_add_white_list (UINT8 addr_type, BD_ADDR bda); + +HCI_API extern BOOLEAN btsnd_hcic_ble_remove_from_white_list (UINT8 addr_type, BD_ADDR bda); + +HCI_API extern BOOLEAN btsnd_hcic_ble_upd_ll_conn_params (UINT16 handle, UINT16 conn_int_min, UINT16 conn_int_max, + UINT16 conn_latency, UINT16 conn_timeout, UINT16 min_len, UINT16 max_len); + +HCI_API extern BOOLEAN btsnd_hcic_ble_set_host_chnl_class (UINT8 chnl_map[HCIC_BLE_CHNL_MAP_SIZE]); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_chnl_map (UINT16 handle); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_remote_feat ( UINT16 handle); + +HCI_API extern BOOLEAN btsnd_hcic_ble_encrypt (UINT8* key, UINT8 key_len, UINT8* plain_text, UINT8 pt_len, void *p_cmd_cplt_cback); + +HCI_API extern BOOLEAN btsnd_hcic_ble_rand (void *p_cmd_cplt_cback); + +HCI_API extern BOOLEAN btsnd_hcic_ble_start_enc ( UINT16 handle, + UINT8 rand[HCIC_BLE_RAND_DI_SIZE], + UINT16 ediv, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]); + +HCI_API extern BOOLEAN btsnd_hcic_ble_ltk_req_reply (UINT16 handle, UINT8 ltk[HCIC_BLE_ENCRYT_KEY_SIZE]); + +HCI_API extern BOOLEAN btsnd_hcic_ble_ltk_req_neg_reply (UINT16 handle); + +HCI_API extern BOOLEAN btsnd_hcic_ble_read_supported_states (void); + + +#endif /* BLE_INCLUDED */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/include/hiddefs.h b/stack/include/hiddefs.h new file mode 100644 index 0000000..e29a4c4 --- /dev/null +++ b/stack/include/hiddefs.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains HID protocol definitions + * + ******************************************************************************/ + +#ifndef HIDDEFS_H +#define HIDDEFS_H + +#include "sdp_api.h" +/* +** tHID_STATUS: HID result codes, returned by HID and device and host functions. +*/ +enum +{ + HID_SUCCESS, + HID_ERR_NOT_REGISTERED, + HID_ERR_ALREADY_REGISTERED, + HID_ERR_NO_RESOURCES, + HID_ERR_NO_CONNECTION, + HID_ERR_INVALID_PARAM, + HID_ERR_UNSUPPORTED, + HID_ERR_UNKNOWN_COMMAND, + HID_ERR_CONGESTED, + HID_ERR_CONN_IN_PROCESS, + HID_ERR_ALREADY_CONN, + HID_ERR_DISCONNECTING, + HID_ERR_SET_CONNABLE_FAIL, + /* Device specific error codes */ + HID_ERR_HOST_UNKNOWN, + HID_ERR_L2CAP_FAILED, + HID_ERR_AUTH_FAILED, + HID_ERR_SDP_BUSY, + + HID_ERR_INVALID = 0xFF +}; + +typedef UINT8 tHID_STATUS; + +#define HID_L2CAP_CONN_FAIL (0x0100) /* Connection Attempt was made but failed */ +#define HID_L2CAP_REQ_FAIL (0x0200) /* L2CAP_ConnectReq API failed */ +#define HID_L2CAP_CFG_FAIL (0x0400) /* L2CAP Configuration was rejected by peer */ + + + +/* Define the HID transaction types +*/ +#define HID_TRANS_HANDSHAKE (0) +#define HID_TRANS_CONTROL (1) +#define HID_TRANS_GET_REPORT (4) +#define HID_TRANS_SET_REPORT (5) +#define HID_TRANS_GET_PROTOCOL (6) +#define HID_TRANS_SET_PROTOCOL (7) +#define HID_TRANS_GET_IDLE (8) +#define HID_TRANS_SET_IDLE (9) +#define HID_TRANS_DATA (10) +#define HID_TRANS_DATAC (11) + +#define HID_GET_TRANS_FROM_HDR(x) ((x >> 4) & 0x0f) +#define HID_GET_PARAM_FROM_HDR(x) (x & 0x0f) +#define HID_BUILD_HDR(t,p) (UINT8)((t << 4) | (p & 0x0f)) + + +/* Parameters for Handshake +*/ +#define HID_PAR_HANDSHAKE_RSP_SUCCESS (0) +#define HID_PAR_HANDSHAKE_RSP_NOT_READY (1) +#define HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID (2) +#define HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ (3) +#define HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM (4) +#define HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN (14) +#define HID_PAR_HANDSHAKE_RSP_ERR_FATAL (15) + + +/* Parameters for Control +*/ +#define HID_PAR_CONTROL_NOP (0) +#define HID_PAR_CONTROL_HARD_RESET (1) +#define HID_PAR_CONTROL_SOFT_RESET (2) +#define HID_PAR_CONTROL_SUSPEND (3) +#define HID_PAR_CONTROL_EXIT_SUSPEND (4) +#define HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG (5) + + +/* Different report types in get, set, data +*/ +#define HID_PAR_REP_TYPE_MASK (0x03) +#define HID_PAR_REP_TYPE_OTHER (0x00) +#define HID_PAR_REP_TYPE_INPUT (0x01) +#define HID_PAR_REP_TYPE_OUTPUT (0x02) +#define HID_PAR_REP_TYPE_FEATURE (0x03) + +/* Parameters for Get Report +*/ + +/* Buffer size in two bytes after Report ID */ +#define HID_PAR_GET_REP_BUFSIZE_FOLLOWS (0x08) + + +/* Parameters for Protocol Type +*/ +#define HID_PAR_PROTOCOL_MASK (0x01) +#define HID_PAR_PROTOCOL_REPORT (0x01) +#define HID_PAR_PROTOCOL_BOOT_MODE (0x00) + +#define HID_PAR_REP_TYPE_MASK (0x03) + +/* Descriptor types in the SDP record +*/ +#define HID_SDP_DESCRIPTOR_REPORT (0x22) +#define HID_SDP_DESCRIPTOR_PHYSICAL (0x23) + +typedef struct desc_info +{ + UINT16 dl_len; + UINT8 *dsc_list; +} tHID_DEV_DSCP_INFO; + +#define HID_SSR_PARAM_INVALID 0xffff + +typedef struct sdp_info +{ + char svc_name[HID_MAX_SVC_NAME_LEN]; /*Service Name */ + char svc_descr[HID_MAX_SVC_DESCR_LEN]; /*Service Description*/ + char prov_name[HID_MAX_PROV_NAME_LEN]; /*Provider Name.*/ + UINT16 rel_num; /*Release Number */ + UINT16 hpars_ver; /*HID Parser Version.*/ + UINT16 ssr_max_latency; /* HIDSSRHostMaxLatency value, if HID_SSR_PARAM_INVALID not used*/ + UINT16 ssr_min_tout; /* HIDSSRHostMinTimeout value, if HID_SSR_PARAM_INVALID not used* */ + UINT8 sub_class; /*Device Subclass.*/ + UINT8 ctry_code; /*Country Code.*/ + UINT16 sup_timeout;/* Supervisory Timeout */ + + tHID_DEV_DSCP_INFO dscp_info; /* Descriptor list and Report list to be set in the SDP record. + This parameter is used if HID_DEV_USE_GLB_SDP_REC is set to FALSE.*/ + tSDP_DISC_REC *p_sdp_layer_rec; +} tHID_DEV_SDP_INFO; + +#endif + diff --git a/stack/include/hidh_api.h b/stack/include/hidh_api.h new file mode 100644 index 0000000..4662630 --- /dev/null +++ b/stack/include/hidh_api.h @@ -0,0 +1,222 @@ +/****************************************************************************** + * + * Copyright (C) 2002-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. + * + ******************************************************************************/ +#ifndef HIDH_API_H +#define HIDH_API_H + +#include "hiddefs.h" +#include "sdp_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +enum { + HID_SDP_NO_SERV_UUID = (SDP_ILLEGAL_PARAMETER+1), + HID_SDP_MANDATORY_MISSING +}; + +/* Attributes mask values to be used in HID_HostAddDev API */ +#define HID_VIRTUAL_CABLE 0x0001 +#define HID_NORMALLY_CONNECTABLE 0x0002 +#define HID_RECONN_INIT 0x0004 +#define HID_SDP_DISABLE 0x0008 +#define HID_BATTERY_POWER 0x0010 +#define HID_REMOTE_WAKE 0x0020 +#define HID_SUP_TOUT_AVLBL 0x0040 +#define HID_SSR_MAX_LATENCY 0x0080 +#define HID_SSR_MIN_TOUT 0x0100 + +#define HID_SEC_REQUIRED 0x8000 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef void (tHID_HOST_SDP_CALLBACK) (UINT16 result, UINT16 attr_mask, + tHID_DEV_SDP_INFO *sdp_rec ); + +/* HID-HOST returns the events in the following table to the application via tHID_HOST_DEV_CALLBACK +HID_HDEV_EVT_OPEN Connected to device with Interrupt and Control Channels in OPEN state. + Data = NA +HID_HDEV_EVT_CLOSE Connection with device is closed. Data=reason code. +HID_HDEV_EVT_RETRYING Lost connection is being re-connected. + Data=Retrial number +HID_HDEV_EVT_IN_REPORT Device sent an input report Data=Report Type pdata= pointer to BT_HDR + (GKI buffer having report data.) +HID_HDEV_EVT_HANDSHAKE Device sent SET_REPORT Data=Result-code pdata=NA. +HID_HDEV_EVT_VC_UNPLUG Device sent Virtual Unplug Data=NA. pdata=NA. +*/ + +enum +{ + HID_HDEV_EVT_OPEN, + HID_HDEV_EVT_CLOSE, + HID_HDEV_EVT_RETRYING, + HID_HDEV_EVT_INTR_DATA, + HID_HDEV_EVT_INTR_DATC, + HID_HDEV_EVT_CTRL_DATA, + HID_HDEV_EVT_CTRL_DATC, + HID_HDEV_EVT_HANDSHAKE, + HID_HDEV_EVT_VC_UNPLUG +}; +typedef void (tHID_HOST_DEV_CALLBACK) (UINT8 dev_handle, + UINT8 event, /* Event from HID-DEVICE. */ + UINT32 data, /* Integer data corresponding to the event.*/ + BT_HDR *p_buf ); /* Pointer data corresponding to the event. */ + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function HID_HostGetSDPRecord +** +** Description This function reads the device SDP record. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostGetSDPRecord (BD_ADDR addr, + tSDP_DISCOVERY_DB *p_db, + UINT32 db_len, + tHID_HOST_SDP_CALLBACK *sdp_cback ); + +/******************************************************************************* +** +** Function HID_HostRegister +** +** Description This function registers HID-Host with lower layers. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback); + +/******************************************************************************* +** +** Function HID_HostDeregister +** +** Description This function is called when the host is about power down. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostDeregister(void); + +/******************************************************************************* +** +** Function HID_HostAddDev +** +** Description This is called so HID-host may manage this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostAddDev (BD_ADDR addr, UINT16 attr_mask, + UINT8 *handle ); + +/******************************************************************************* +** +** Function HID_HostRemoveDev +** +** Description This removes the device from list devices that host has to manage. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostRemoveDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostOpenDev +** +** Description This function is called when the user wants to initiate a +** connection attempt to a device. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostOpenDev (UINT8 dev_handle ); + +/******************************************************************************* +** +** Function HID_HostWriteDev +** +** Description This function is called when the host has a report to send. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostWriteDev(UINT8 dev_handle, UINT8 t_type, + UINT8 param, UINT16 data, + UINT8 report_id, BT_HDR *pbuf); + +/******************************************************************************* +** +** Function HID_HostCloseDev +** +** Description This function disconnects the device. +** +** Returns void +** +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostCloseDev(UINT8 dev_handle ); + +/******************************************************************************* +** Function HID_HostInit +** +** Description This function initializes the control block and trace variable +** +** Returns void +*******************************************************************************/ +HID_API extern void HID_HostInit(void); + +/******************************************************************************* +** Function HID_HostSetSecurityLevel +** +** Description This function sets the security level for the devices which +** are marked by application as requiring security +** +** Returns tHID_STATUS +*******************************************************************************/ +HID_API extern tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ); + +/******************************************************************************* +** +** Function HID_HostSetTraceLevel +** +** Description This function sets the trace level for HID Host. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +HID_API extern UINT8 HID_HostSetTraceLevel (UINT8 new_level); + +#ifdef __cplusplus +} +#endif + +#endif /* HIDH_API_H */ diff --git a/stack/include/l2c_api.h b/stack/include/l2c_api.h new file mode 100644 index 0000000..618ea8d --- /dev/null +++ b/stack/include/l2c_api.h @@ -0,0 +1,1185 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the L2CAP API definitions + * + ******************************************************************************/ +#ifndef L2C_API_H +#define L2C_API_H + +#include "bt_target.h" +#include "l2cdefs.h" +#include "hcidefs.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Define the minimum offset that L2CAP needs in a buffer. This is made up of +** HCI type(1), len(2), handle(2), L2CAP len(2) and CID(2) => 9 +*/ +#define L2CAP_MIN_OFFSET 13 /* plus control(2), SDU length(2) */ + +/* Minimum offset for broadcast needs another two bytes for the PSM */ +#define L2CAP_BCST_MIN_OFFSET 11 + +/* ping result codes */ +#define L2CAP_PING_RESULT_OK 0 /* Ping reply received OK */ +#define L2CAP_PING_RESULT_NO_LINK 1 /* Link could not be setup */ +#define L2CAP_PING_RESULT_NO_RESP 2 /* Remote L2CAP did not reply */ + +/* result code for L2CA_DataWrite() */ +#define L2CAP_DW_FAILED FALSE +#define L2CAP_DW_SUCCESS TRUE +#define L2CAP_DW_CONGESTED 2 + +/* Values for priority parameter to L2CA_SetAclPriority */ +#define L2CAP_PRIORITY_NORMAL 0 +#define L2CAP_PRIORITY_HIGH 1 + +/* Values for priority parameter to L2CA_SetTxPriority */ +#define L2CAP_CHNL_PRIORITY_HIGH 0 +#define L2CAP_CHNL_PRIORITY_MEDIUM 1 +#define L2CAP_CHNL_PRIORITY_LOW 2 + +typedef UINT8 tL2CAP_CHNL_PRIORITY; + +/* Values for Tx/Rx data rate parameter to L2CA_SetChnlDataRate */ +#define L2CAP_CHNL_DATA_RATE_HIGH 3 +#define L2CAP_CHNL_DATA_RATE_MEDIUM 2 +#define L2CAP_CHNL_DATA_RATE_LOW 1 +#define L2CAP_CHNL_DATA_RATE_NO_TRAFFIC 0 + +typedef UINT8 tL2CAP_CHNL_DATA_RATE; + +/* Data Packet Flags (bits 2-15 are reserved) */ +/* layer specific 14-15 bits are used for FCR SAR */ +#define L2CAP_FLUSHABLE_MASK 0x0003 +#define L2CAP_FLUSHABLE_CH_BASED 0x0000 +#define L2CAP_FLUSHABLE_PKT 0x0001 +#define L2CAP_NON_FLUSHABLE_PKT 0x0002 + + +/* L2CA_FlushChannel num_to_flush definitions */ +#define L2CAP_FLUSH_CHANS_ALL 0xffff +#define L2CAP_FLUSH_CHANS_GET 0x0000 + + +/* special CID for Multi-AV for reporting congestion */ +#define L2CAP_MULTI_AV_CID 0 + +/* length of the HCI header block */ +/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) */ +#define L2CAP_MULTI_AV_HCI_HDR_LEN 8 + +/* length of padding for 4 bytes align */ +#define L2CAP_MULTI_AV_PADDING_LEN 2 + +/* length of the HCI header block with padding for FCR */ +/* HCI header(4) + SNK count(1) + FCR bits(1) + AV data length(2) + padding(2) */ +#define L2CAP_MULTI_AV_HCI_HDR_LEN_WITH_PADDING 10 + +/* length of the L2CAP header block */ +/* HCI header(4) + L2CAP header(4) + padding(4) or control word(2) + FCS(2) */ +#define L2CAP_MULTI_AV_L2C_HDR_LEN 12 + +/* definition used for L2CA_SetDesireRole */ +#define L2CAP_ROLE_SLAVE HCI_ROLE_SLAVE +#define L2CAP_ROLE_MASTER HCI_ROLE_MASTER +#define L2CAP_ROLE_ALLOW_SWITCH 0x80 /* set this bit to allow switch at create conn */ +#define L2CAP_ROLE_DISALLOW_SWITCH 0x40 /* set this bit to disallow switch at create conn */ +#define L2CAP_ROLE_CHECK_SWITCH 0xC0 + + +/* Values for 'allowed_modes' field passed in structure tL2CAP_ERTM_INFO +*/ +#define L2CAP_FCR_CHAN_OPT_BASIC (1 << L2CAP_FCR_BASIC_MODE) +#define L2CAP_FCR_CHAN_OPT_ERTM (1 << L2CAP_FCR_ERTM_MODE) +#define L2CAP_FCR_CHAN_OPT_STREAM (1 << L2CAP_FCR_STREAM_MODE) + +#define L2CAP_FCR_CHAN_OPT_ALL_MASK (L2CAP_FCR_CHAN_OPT_BASIC | L2CAP_FCR_CHAN_OPT_ERTM | L2CAP_FCR_CHAN_OPT_STREAM) + +/* Validity check for PSM. PSM values must be odd. Also, all PSM values must +** be assigned such that the least significant bit of the most sigificant +** octet equals zero. +*/ +#define L2C_INVALID_PSM(psm) (((psm) & 0x0101) != 0x0001) +#define L2C_IS_VALID_PSM(psm) (((psm) & 0x0101) == 0x0001) + +#if (BLE_INCLUDED == TRUE) +#define L2CAP_LE_INT_MIN 0x0006 +#define L2CAP_LE_INT_MAX 0x0C80 +#define L2CAP_LE_LATENCY_MAX 500 +#define L2CAP_LE_TIMEOUT_MIN 0x000a +#define L2CAP_LE_TIMEOUT_MAX 0x0C80 +#define L2CAP_LE_TIMEOUT_DEFAULT 0x07D0 +#endif + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +typedef struct +{ +#define L2CAP_FCR_BASIC_MODE 0x00 +#define L2CAP_FCR_ERTM_MODE 0x03 +#define L2CAP_FCR_STREAM_MODE 0x04 + + UINT8 mode; + + UINT8 tx_win_sz; + UINT8 max_transmit; + UINT16 rtrans_tout; + UINT16 mon_tout; + UINT16 mps; +} tL2CAP_FCR_OPTS; + +/* Define a structure to hold the configuration parameters. Since the +** parameters are optional, for each parameter there is a boolean to +** use to signify its presence or absence. +*/ +typedef struct +{ + UINT16 result; /* Only used in confirm messages */ + BOOLEAN mtu_present; + UINT16 mtu; + BOOLEAN qos_present; + FLOW_SPEC qos; + BOOLEAN flush_to_present; + UINT16 flush_to; + BOOLEAN fcr_present; + tL2CAP_FCR_OPTS fcr; + BOOLEAN fcs_present; /* Optionally bypasses FCS checks */ + UINT8 fcs; /* '0' if desire is to bypass FCS, otherwise '1' */ + BOOLEAN ext_flow_spec_present; + tHCI_EXT_FLOW_SPEC ext_flow_spec; + UINT16 flags; /* bit 0: 0-no continuation, 1-continuation */ +} tL2CAP_CFG_INFO; + +/* L2CAP channel configured field bitmap */ +#define L2CAP_CH_CFG_MASK_MTU 0x0001 +#define L2CAP_CH_CFG_MASK_QOS 0x0002 +#define L2CAP_CH_CFG_MASK_FLUSH_TO 0x0004 +#define L2CAP_CH_CFG_MASK_FCR 0x0008 +#define L2CAP_CH_CFG_MASK_FCS 0x0010 +#define L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC 0x0020 + +typedef UINT16 tL2CAP_CH_CFG_BITS; + +/********************************* +** Callback Functions Prototypes +**********************************/ + +/* Connection indication callback prototype. Parameters are +** BD Address of remote +** Local CID assigned to the connection +** PSM that the remote wants to connect to +** Identifier that the remote sent +*/ +typedef void (tL2CA_CONNECT_IND_CB) (BD_ADDR, UINT16, UINT16, UINT8); + + +/* Connection confirmation callback prototype. Parameters are +** Local CID +** Result - 0 = connected, non-zero means failure reason +*/ +typedef void (tL2CA_CONNECT_CFM_CB) (UINT16, UINT16); + + +/* Connection pending callback prototype. Parameters are +** Local CID +*/ +typedef void (tL2CA_CONNECT_PND_CB) (UINT16); + + +/* Configuration indication callback prototype. Parameters are +** Local CID assigned to the connection +** Pointer to configuration info +*/ +typedef void (tL2CA_CONFIG_IND_CB) (UINT16, tL2CAP_CFG_INFO *); + + +/* Configuration confirm callback prototype. Parameters are +** Local CID assigned to the connection +** Pointer to configuration info +*/ +typedef void (tL2CA_CONFIG_CFM_CB) (UINT16, tL2CAP_CFG_INFO *); + + +/* Disconnect indication callback prototype. Parameters are +** Local CID +** Boolean whether upper layer should ack this +*/ +typedef void (tL2CA_DISCONNECT_IND_CB) (UINT16, BOOLEAN); + + +/* Disconnect confirm callback prototype. Parameters are +** Local CID +** Result +*/ +typedef void (tL2CA_DISCONNECT_CFM_CB) (UINT16, UINT16); + + +/* QOS Violation indication callback prototype. Parameters are +** BD Address of violating device +*/ +typedef void (tL2CA_QOS_VIOLATION_IND_CB) (BD_ADDR); + + +/* Data received indication callback prototype. Parameters are +** Local CID +** Address of buffer +*/ +typedef void (tL2CA_DATA_IND_CB) (UINT16, BT_HDR *); + + +/* Echo response callback prototype. Note that this is not included in the +** registration information, but is passed to L2CAP as part of the API to +** actually send an echo request. Parameters are +** Result +*/ +typedef void (tL2CA_ECHO_RSP_CB) (UINT16); + + +/* Callback function prototype to pass broadcom specific echo response */ +/* to the upper layer */ +typedef void (tL2CA_ECHO_DATA_CB) (BD_ADDR, UINT16, UINT8 *); + + +/* Congestion status callback protype. This callback is optional. If +** an application tries to send data when the transmit queue is full, +** the data will anyways be dropped. The parameter is: +** Local CID +** TRUE if congested, FALSE if uncongested +*/ +typedef void (tL2CA_CONGESTION_STATUS_CB) (UINT16, BOOLEAN); + +/* Callback prototype for number of packets completed events. +** This callback notifies the application when Number of Completed Packets +** event has been received. +** This callback is originally designed for 3DG devices. +** The parameter is: +** peer BD_ADDR +*/ +typedef void (tL2CA_NOCP_CB) (BD_ADDR); + +/* Transmit complete callback protype. This callback is optional. If +** set, L2CAP will call it when packets are sent or flushed. If the +** count is 0xFFFF, it means all packets are sent for that CID (eRTM +** mode only). The parameters are: +** Local CID +** Number of SDUs sent or dropped +*/ +typedef void (tL2CA_TX_COMPLETE_CB) (UINT16, UINT16); + +/* Define the structure that applications use to register with +** L2CAP. This structure includes callback functions. All functions +** MUST be provided, with the exception of the "connect pending" +** callback and "congestion status" callback. +*/ +typedef struct +{ + tL2CA_CONNECT_IND_CB *pL2CA_ConnectInd_Cb; + tL2CA_CONNECT_CFM_CB *pL2CA_ConnectCfm_Cb; + tL2CA_CONNECT_PND_CB *pL2CA_ConnectPnd_Cb; + tL2CA_CONFIG_IND_CB *pL2CA_ConfigInd_Cb; + tL2CA_CONFIG_CFM_CB *pL2CA_ConfigCfm_Cb; + tL2CA_DISCONNECT_IND_CB *pL2CA_DisconnectInd_Cb; + tL2CA_DISCONNECT_CFM_CB *pL2CA_DisconnectCfm_Cb; + tL2CA_QOS_VIOLATION_IND_CB *pL2CA_QoSViolationInd_Cb; + tL2CA_DATA_IND_CB *pL2CA_DataInd_Cb; + tL2CA_CONGESTION_STATUS_CB *pL2CA_CongestionStatus_Cb; + tL2CA_TX_COMPLETE_CB *pL2CA_TxComplete_Cb; + +} tL2CAP_APPL_INFO; + +/* Define the structure that applications use to create or accept +** connections with enhanced retransmission mode. +*/ +typedef struct +{ + UINT8 preferred_mode; + UINT8 allowed_modes; + UINT8 user_rx_pool_id; + UINT8 user_tx_pool_id; + UINT8 fcr_rx_pool_id; + UINT8 fcr_tx_pool_id; + +} tL2CAP_ERTM_INFO; + +#define L2CA_REGISTER(a,b,c) L2CA_Register(a,(tL2CAP_APPL_INFO *)b) +#define L2CA_DEREGISTER(a) L2CA_Deregister(a) +#define L2CA_CONNECT_REQ(a,b,c,d) L2CA_ErtmConnectReq(a,b,c) +#define L2CA_CONNECT_RSP(a,b,c,d,e,f,g) L2CA_ErtmConnectRsp(a,b,c,d,e,f) +#define L2CA_CONFIG_REQ(a,b) L2CA_ConfigReq(a,b) +#define L2CA_CONFIG_RSP(a,b) L2CA_ConfigRsp(a,b) +#define L2CA_DISCONNECT_REQ(a) L2CA_DisconnectReq(a) +#define L2CA_DISCONNECT_RSP(a) L2CA_DisconnectRsp(a) +#define L2CA_DATA_WRITE(a, b) L2CA_DataWrite(a, b) + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function L2CA_Register +** +** Description Other layers call this function to register for L2CAP +** services. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectReq() and +** BTM_SetSecurityLevel(). +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info); + +/******************************************************************************* +** +** Function L2CA_Deregister +** +** Description Other layers call this function to deregister for L2CAP +** services. +** +** Returns void +** +*******************************************************************************/ +L2C_API extern void L2CA_Deregister (UINT16 psm); + +/******************************************************************************* +** +** Function L2CA_AllocatePSM +** +** Description Other layers call this function to find an unused PSM for L2CAP +** services. +** +** Returns PSM to use. +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_AllocatePSM(void); + +/******************************************************************************* +** +** Function L2CA_ConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr); + +/******************************************************************************* +** +** Function L2CA_ConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, + UINT16 result, UINT16 status); + +/******************************************************************************* +** +** Function L2CA_ErtmConnectReq +** +** Description Higher layers call this function to create an L2CAP connection +** that needs to use Enhanced Retransmission Mode. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, + tL2CAP_ERTM_INFO *p_ertm_info); + +/******************************************************************************* +** +** Function L2CA_ErtmConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback, and for which the higher layer wants +** to use Enhanced Retransmission Mode. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, + UINT16 result, UINT16 status, + tL2CAP_ERTM_INFO *p_ertm_info); + +/******************************************************************************* +** +** Function L2CA_ConfigReq +** +** Description Higher layers call this function to send configuration. +** +** Returns TRUE if configuration sent, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_ConfigRsp +** +** Description Higher layers call this function to send a configuration +** response. +** +** Returns TRUE if configuration response sent, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg); + +/******************************************************************************* +** +** Function L2CA_DisconnectReq +** +** Description Higher layers call this function to disconnect a channel. +** +** Returns TRUE if disconnect sent, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_DisconnectReq (UINT16 cid); + +/******************************************************************************* +** +** Function L2CA_DisconnectRsp +** +** Description Higher layers call this function to acknowledge the +** disconnection of a channel. +** +** Returns void +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_DisconnectRsp (UINT16 cid); + +/******************************************************************************* +** +** Function L2CA_DataWrite +** +** Description Higher layers call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data); + +/******************************************************************************* +** +** Function L2CA_Ping +** +** Description Higher layers call this function to send an echo request. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_cb); + +/******************************************************************************* +** +** Function L2CA_Echo +** +** Description Higher layers call this function to send an echo request +** with application-specific data. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback); + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeout +** +** 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 TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, + BOOLEAN is_global); + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeoutByBdAddr +** +** Description Higher layers call this function to set the idle timeout for +** a connection. 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. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout); + +/******************************************************************************* +** +** Function L2CA_SetTraceLevel +** +** Description This function sets the trace level for L2CAP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_SetTraceLevel (UINT8 trace_level); + +/******************************************************************************* +** +** Function L2CA_SetDesireRole +** +** Description This function sets the desire role for L2CAP. +** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on +** HciCreateConnection. +** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on +** HciCreateConnection. +** +** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE), +** the desire role is set to the new value. Otherwise, it is not changed. +** +** Returns the new (current) role +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_SetDesireRole (UINT8 new_role); + +/******************************************************************************* +** +** Function L2CA_LocalLoopbackReq +** +** Description This function sets up a CID for local loopback +** +** Returns CID of 0 if none. +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr); + +/******************************************************************************* +** +** Function L2CA_FlushChannel +** +** Description This function flushes none, some or all buffers queued up +** for xmission for a particular CID. If called with +** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number +** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff) +** flushes all buffers. All other values specifies the maximum +** buffers to flush. +** +** Returns Number of buffers left queued for that CID +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush); + + +/******************************************************************************* +** +** Function L2CA_SetAclPriority +** +** Description Sets the transmission priority for an ACL channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority); + +/******************************************************************************* +** +** Function L2CA_FlowControl +** +** Description Higher layers call this function to flow control a channel. +** +** data_enabled - TRUE data flows, FALSE data is stopped +** +** Returns TRUE if valid channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled); + +/******************************************************************************* +** +** Function L2CA_SendTestSFrame +** +** Description Higher layers call this function to send a test S-frame. +** +** Returns TRUE if valid Channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SendTestSFrame (UINT16 cid, BOOLEAN rr_or_rej, + UINT8 back_track); + +/******************************************************************************* +** +** Function L2CA_SetTxPriority +** +** Description Sets the transmission priority for a channel. (FCR Mode) +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority); + +/******************************************************************************* +** +** Function L2CA_RegForNoCPEvt +** +** Description Register callback for Number of Completed Packets event. +** +** Input Param p_cb - callback for Number of completed packets event +** p_bda - BT address of remote device +** +** Returns +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda); + +/******************************************************************************* +** +** Function L2CA_SetChnlDataRate +** +** Description Sets the tx/rx data rate for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx); + +typedef void (tL2CA_RESERVE_CMPL_CBACK) (void); + +/******************************************************************************* +** +** Function L2CA_SetFlushTimeout +** +** Description This function set the automatic flush time out in Baseband +** for ACL-U packets. +** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY +** then the flush time out will be applied to all ACL link. +** FlushTimeout: flush time out in ms +** 0x0000 : No automatic flush +** L2CAP_NO_RETRANSMISSION : No retransmission +** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5) +** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot). +** Otherwise, return FALSE. +** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This flush timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout); + +/******************************************************************************* +** +** Function L2CA_DataWriteEx +** +** Description Higher layers call this function to write data with extended +** flags. +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags); + +/******************************************************************************* +** +** Function L2CA_SetChnlFlushability +** +** Description Higher layers call this function to set a channels +** flushability flags +** +** Returns TRUE if CID found, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable); + +/******************************************************************************* +** +** Function L2CA_GetPeerFeatures +** +** Description Get a peers features and fixed channel map +** +** Parameters: BD address of the peer +** Pointers to features and channel mask storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask); + +/******************************************************************************* +** +** Function L2CA_GetBDAddrbyHandle +** +** Description Get BD address for the given HCI handle +** +** Parameters: HCI handle +** BD address of the peer +** +** Return value: TRUE if found lcb for the given handle, FALSE otherwise +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function L2CA_GetChnlFcrMode +** +** Description Get the channel FCR mode +** +** Parameters: Local CID +** +** Return value: Channel mode +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_GetChnlFcrMode (UINT16 lcid); + + +/******************************************************************************* +** +** UCD callback prototypes +** +*******************************************************************************/ + +/* UCD discovery. Parameters are +** BD Address of remote +** Data Type +** Data +*/ +#define L2CAP_UCD_INFO_TYPE_RECEPTION 0x01 +#define L2CAP_UCD_INFO_TYPE_MTU 0x02 + +typedef void (tL2CA_UCD_DISCOVER_CB) (BD_ADDR, UINT8, UINT32); + +/* UCD data received. Parameters are +** BD Address of remote +** Pointer to buffer with data +*/ +typedef void (tL2CA_UCD_DATA_CB) (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, +** the data will anyways be dropped. The parameter is: +** remote BD_ADDR +** TRUE if congested, FALSE if uncongested +*/ +typedef void (tL2CA_UCD_CONGESTION_STATUS_CB) (BD_ADDR, BOOLEAN); + +/* UCD registration info (the callback addresses and PSM) +*/ +typedef struct +{ + tL2CA_UCD_DISCOVER_CB *pL2CA_UCD_Discover_Cb; + tL2CA_UCD_DATA_CB *pL2CA_UCD_Data_Cb; + tL2CA_UCD_CONGESTION_STATUS_CB *pL2CA_UCD_Congestion_Status_Cb; +} tL2CAP_UCD_CB_INFO; + +/******************************************************************************* +** +** Function L2CA_UcdRegister +** +** Description Register PSM on UCD. +** +** Parameters: tL2CAP_UCD_CB_INFO +** +** Return value: TRUE if successs +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info ); + +/******************************************************************************* +** +** Function L2CA_UcdDeregister +** +** Description Deregister PSM on UCD. +** +** Parameters: PSM +** +** Return value: TRUE if successs +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UcdDeregister ( UINT16 psm ); + +/******************************************************************************* +** +** Function L2CA_UcdDiscover +** +** Description Discover UCD of remote device. +** +** Parameters: PSM +** BD_ADDR of remote device +** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION +** L2CAP_UCD_INFO_TYPE_MTU +** +** +** Return value: TRUE if successs +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type ); + +/******************************************************************************* +** +** Function L2CA_UcdDataWrite +** +** Description Send UCD to remote device +** +** Parameters: PSM +** BD Address of remote +** Pointer to buffer of type BT_HDR +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags); + +/******************************************************************************* +** +** Function L2CA_UcdSetIdleTimeout +** +** Description Set UCD Idle timeout. +** +** Parameters: BD Addr +** Timeout in second +** +** Return value: TRUE if successs +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout ); + +/******************************************************************************* +** +** Function L2CA_UCDSetTxPriority +** +** Description Sets the transmission priority for a connectionless channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority ); + + +/******************************************************************************* +** +** Fixed Channel callback prototypes +** +*******************************************************************************/ + +/* Fixed channel connected and disconnected. Parameters are +** BD Address of remote +** TRUE if channel is connected, FALSE if disconnected +** Reason for connection failure +*/ +typedef void (tL2CA_FIXED_CHNL_CB) (BD_ADDR, BOOLEAN, UINT16); + +/* Signalling data received. Parameters are +** BD Address of remote +** Pointer to buffer with data +*/ +typedef void (tL2CA_FIXED_DATA_CB) (BD_ADDR, BT_HDR *); + +/* Fixed channel registration info (the callback addresses and channel config) +*/ +typedef struct +{ + tL2CA_FIXED_CHNL_CB *pL2CA_FixedConn_Cb; + tL2CA_FIXED_DATA_CB *pL2CA_FixedData_Cb; + tL2CAP_FCR_OPTS fixed_chnl_opts; + + UINT16 default_idle_tout; +} tL2CAP_FIXED_CHNL_REG; + + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function L2CA_RegisterFixedChannel +** +** Description Register a fixed channel. +** +** Parameters: Fixed Channel # +** Channel Callbacks and config +** +** Return value: TRUE if registered OK +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg); + +/******************************************************************************* +** +** Function L2CA_ConnectFixedChnl +** +** Description Connect an fixed signalling channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** +** Return value: TRUE if connection started +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function L2CA_SendFixedChnlData +** +** Description Write data on a fixed signalling channel. +** +** Parameters: Fixed CID +** BD Address of remote +** Pointer to buffer of type BT_HDR +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function L2CA_RemoveFixedChnl +** +** Description Remove a fixed channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** Idle timeout to use (or 0xFFFF if don't care) +** +** Return value: TRUE if channel removed +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda); + +/******************************************************************************* +** +** Function L2CA_SetFixedChannelTout +** +** Description Higher layers call this function to set the idle timeout for +** a fixed channel. 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. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout); + +#endif /* (L2CAP_NUM_FIXED_CHNLS > 0) */ + +/******************************************************************************* +** +** Function L2CA_GetCurrentConfig +** +** Description This function returns configurations of L2CAP channel +** pp_our_cfg : pointer of our saved configuration options +** p_our_cfg_bits : valid config in bitmap +** pp_peer_cfg: pointer of peer's saved configuration options +** p_peer_cfg_bits : valid config in bitmap +** +** Returns TRUE if successful +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid, + tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits, + tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits); + +#if (L2CAP_CORRUPT_ERTM_PKTS == TRUE) +/******************************************************************************* +** +** Function L2CA_SetupErtmTest +** +** Description This function is used for testing purposes only. +** It corrupts or drops one or more packets used with ERTM channels. +** +** Parameters +** cid - channel ID (0 uses RFCOMM PSM's CID) +** +** type - type of test to run (L2CAP_FCR_TTYPE_CORR_IFRAMES +** L2CAP_FCR_TTYPE_CORR_SFRAME +** L2CAP_FCR_TTYPE_STOP_TEST +** L2CAP_FCR_TTYPE_GET_CID - returns rfcomm cid only) +** +** is_rx - TRUE to corrupt Rx packet, FALSE for Tx packet) +** +** freq - L2CAP_FCR_FREQ_RANDOM (turns on random corruptions/drops) +** L2CAP_FCR_FREQ_NORMAL (turns on test with "count" corruptions/drops) +** +** count - number of packets in a row to drop or corrupt +** +** Returns CID of channel running test +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_SetupErtmTest (UINT16 cid, UINT8 type, BOOLEAN is_rx, UINT8 freq, UINT16 count); + +/******************************************************************************* +** +** Function L2CA_SendPolledSFrame +** +** Description This function is used for testing purposes only. +** It Sends a Polled RR or RNR to the peer +** +** Parameters +** cid - channel ID +** +** sup_type - (L2CAP_FCR_SUP_RR or L2CAP_FCR_SUP_RNR) +** +** Returns void +** +*******************************************************************************/ +L2C_API extern void L2CA_SendPolledSFrame (UINT16 cid, UINT16 sup_type); + +/******************************************************************************* +** +** Function L2CA_BypassSFrame +** +** Description This function is used for testing purposes only. +** It skips sending 'count' S-Frames. +** +** Parameters +** cid - channel ID +** +** count - Number of S-Frames to skip sending +** +** Returns void +** +*******************************************************************************/ +L2C_API extern void L2CA_BypassSFrame (UINT16 cid, UINT8 count); + +#endif /* (L2CAP_CORRUPT_ERTM_PKTS == TRUE) */ + + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function L2CA_CancelBleConnectReq +** +** Description Cancel a pending connection attempt to a BLE device. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if connection was cancelled +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda); + +/******************************************************************************* +** +** Function L2CA_UpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bdRa, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); + +/******************************************************************************* +** +** Function L2CA_EnableUpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** enable flag +** +** Return value: TRUE if update started +** +*******************************************************************************/ +L2C_API extern BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable); + +/******************************************************************************* +** +** Function L2CA_GetBleConnRole +** +** Description This function returns the connection role. +** +** Returns link role. +** +*******************************************************************************/ +L2C_API extern UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function L2CA_GetDisconnectReason +** +** Description This function returns the disconnect reason code. +** +** Returns disconnect reason +** +*******************************************************************************/ +L2C_API extern UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda); + +#endif /* (BLE_INCLUDED == TRUE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* L2C_API_H */ diff --git a/stack/include/l2cdefs.h b/stack/include/l2cdefs.h new file mode 100644 index 0000000..fe41476 --- /dev/null +++ b/stack/include/l2cdefs.h @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef L2CDEFS_H +#define L2CDEFS_H + +/* L2CAP command codes +*/ +#define L2CAP_CMD_REJECT 0x01 +#define L2CAP_CMD_CONN_REQ 0x02 +#define L2CAP_CMD_CONN_RSP 0x03 +#define L2CAP_CMD_CONFIG_REQ 0x04 +#define L2CAP_CMD_CONFIG_RSP 0x05 +#define L2CAP_CMD_DISC_REQ 0x06 +#define L2CAP_CMD_DISC_RSP 0x07 +#define L2CAP_CMD_ECHO_REQ 0x08 +#define L2CAP_CMD_ECHO_RSP 0x09 +#define L2CAP_CMD_INFO_REQ 0x0A +#define L2CAP_CMD_INFO_RSP 0x0B +#define L2CAP_CMD_AMP_CONN_REQ 0x0C +#define L2CAP_CMD_AMP_CONN_RSP 0x0D +#define L2CAP_CMD_AMP_MOVE_REQ 0x0E +#define L2CAP_CMD_AMP_MOVE_RSP 0x0F +#define L2CAP_CMD_AMP_MOVE_CFM 0x10 +#define L2CAP_CMD_AMP_MOVE_CFM_RSP 0x11 +#define L2CAP_CMD_BLE_UPDATE_REQ 0x12 +#define L2CAP_CMD_BLE_UPDATE_RSP 0x13 + + +/* Define some packet and header lengths +*/ +#define L2CAP_PKT_OVERHEAD 4 /* Length and CID */ +#define L2CAP_CMD_OVERHEAD 4 /* Cmd code, Id and length */ +#define L2CAP_CMD_REJECT_LEN 2 /* Reason (data is optional) */ +#define L2CAP_CONN_REQ_LEN 4 /* PSM and source CID */ +#define L2CAP_CONN_RSP_LEN 8 /* Dest CID, source CID, reason, status */ +#define L2CAP_CONFIG_REQ_LEN 4 /* Dest CID, flags (data is optional) */ +#define L2CAP_CONFIG_RSP_LEN 6 /* Dest CID, flags, result,data optional*/ +#define L2CAP_DISC_REQ_LEN 4 /* Dest CID, source CID */ +#define L2CAP_DISC_RSP_LEN 4 /* Dest CID, source CID */ +#define L2CAP_ECHO_REQ_LEN 0 /* Data is optional */ +#define L2CAP_ECHO_RSP_LEN 0 /* Data is optional */ +#define L2CAP_INFO_REQ_LEN 2 /* Info type */ +#define L2CAP_INFO_RSP_LEN 4 /* Info type, result (data is optional) */ +#define L2CAP_BCST_OVERHEAD 2 /* Additional broadcast packet overhead */ +#define L2CAP_UCD_OVERHEAD 2 /* Additional connectionless packet overhead */ + +#define L2CAP_AMP_CONN_REQ_LEN 5 /* PSM, CID, and remote controller ID */ +#define L2CAP_AMP_MOVE_REQ_LEN 3 /* CID and remote controller ID */ +#define L2CAP_AMP_MOVE_RSP_LEN 4 /* CID and result */ +#define L2CAP_AMP_MOVE_CFM_LEN 4 /* CID and result */ +#define L2CAP_AMP_MOVE_CFM_RSP_LEN 2 /* CID */ + +#define L2CAP_CMD_BLE_UPD_REQ_LEN 8 /* Min and max interval, latency, tout */ +#define L2CAP_CMD_BLE_UPD_RSP_LEN 2 /* Result */ + + +/* Define the packet boundary flags +*/ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +#define L2CAP_PKT_START_FLUSHABLE 2 +#define L2CAP_PKT_START_NON_FLUSHABLE 0 +#endif +#define L2CAP_COMPLETE_AMP_PKT 3 /* complete L2CAP packet on AMP HCI */ +#define L2CAP_PKT_START 2 +#define L2CAP_PKT_CONTINUE 1 +#define L2CAP_MASK_FLAG 0x0FFF +#define L2CAP_PKT_TYPE_SHIFT 12 +#define L2CAP_PKT_TYPE_MASK 3 + + +/* Define the L2CAP connection result codes +*/ +#define L2CAP_CONN_OK 0 +#define L2CAP_CONN_PENDING 1 +#define L2CAP_CONN_NO_PSM 2 +#define L2CAP_CONN_SECURITY_BLOCK 3 +#define L2CAP_CONN_NO_RESOURCES 4 +#define L2CAP_CONN_BAD_CTLR_ID 5 /* AMP related */ +#define L2CAP_CONN_TIMEOUT 0xEEEE +#define L2CAP_CONN_AMP_FAILED 254 +#define L2CAP_CONN_NO_LINK 255 /* Add a couple of our own for internal use */ +#define L2CAP_CONN_CANCEL 256 /* L2CAP connection cancelled */ + + +/* Define L2CAP Move Channel Response result codes +*/ +#define L2CAP_MOVE_OK 0 +#define L2CAP_MOVE_PENDING 1 +#define L2CAP_MOVE_CTRL_ID_NOT_SUPPORT 2 +#define L2CAP_MOVE_SAME_CTRLR_ID 3 +#define L2CAP_MOVE_CONFIG_NOT_SUPPORTED 4 +#define L2CAP_MOVE_CHAN_COLLISION 5 +#define L2CAP_MOVE_NOT_ALLOWED 6 + + +/* Define L2CAP Move Channel Confirmation result codes +*/ +#define L2CAP_MOVE_CFM_OK 0 +#define L2CAP_MOVE_CFM_REFUSED 1 + + +/* Define the L2CAP command reject reason codes +*/ +#define L2CAP_CMD_REJ_NOT_UNDERSTOOD 0 +#define L2CAP_CMD_REJ_MTU_EXCEEDED 1 +#define L2CAP_CMD_REJ_INVALID_CID 2 + + +/* L2CAP Predefined CIDs (0x0004-0x003E Reserved) +*/ +#define L2CAP_SIGNALLING_CID 1 +#define L2CAP_CONNECTIONLESS_CID 2 +#define L2CAP_AMP_CID 3 +#define L2CAP_ATT_CID 4 +#define L2CAP_BLE_SIGNALLING_CID 5 +#define L2CAP_SMP_CID 6 +#define L2CAP_AMP_TEST_CID 0x003F +#define L2CAP_BASE_APPL_CID 0x0040 + +/* Fixed Channels mask bits +*/ +#define L2CAP_FIXED_CHNL_SIG_BIT (1 << L2CAP_SIGNALLING_CID) /* Signal Channel Supported (Mandatory) */ +#define L2CAP_FIXED_CHNL_CNCTLESS_BIT (1 << L2CAP_CONNECTIONLESS_CID) /* Connectionless Reception */ +#define L2CAP_FIXED_CHNL_AMP_BIT (1 << L2CAP_AMP_CID) /* AMP Manager Supported */ +#define L2CAP_FIXED_CHNL_ATT_BIT (1 << L2CAP_ATT_CID) /* Attribute protocol Supported */ +#define L2CAP_FIXED_CHNL_BLE_SIG_BIT (1 << L2CAP_BLE_SIGNALLING_CID) /* BLE Signalling Supported */ +#define L2CAP_FIXED_CHNL_SMP_BIT (1 << L2CAP_SMP_CID) /* BLE Security Manager Supported */ + + + +/* Define the L2CAP configuration result codes +*/ +#define L2CAP_CFG_OK 0 +#define L2CAP_CFG_UNACCEPTABLE_PARAMS 1 +#define L2CAP_CFG_FAILED_NO_REASON 2 +#define L2CAP_CFG_UNKNOWN_OPTIONS 3 +#define L2CAP_CFG_PENDING 4 +#define L2CAP_CFG_FLOW_SPEC_REJECTED 5 + + +/* Define the L2CAP configuration option types +*/ +#define L2CAP_CFG_TYPE_MTU 0x01 +#define L2CAP_CFG_TYPE_FLUSH_TOUT 0x02 +#define L2CAP_CFG_TYPE_QOS 0x03 +#define L2CAP_CFG_TYPE_FCR 0x04 +#define L2CAP_CFG_TYPE_FCS 0x05 +#define L2CAP_CFG_TYPE_EXT_FLOW 0x06 +#define L2CAP_CFG_TYPE_EXT_WIN_SIZE 0x07 + +#define L2CAP_CFG_MTU_OPTION_LEN 2 /* MTU option length */ +#define L2CAP_CFG_FLUSH_OPTION_LEN 2 /* Flush option len */ +#define L2CAP_CFG_QOS_OPTION_LEN 22 /* QOS option length */ +#define L2CAP_CFG_FCR_OPTION_LEN 9 /* FCR option length */ +#define L2CAP_CFG_FCS_OPTION_LEN 1 /* FCR option length */ +#define L2CAP_CFG_EXT_FLOW_OPTION_LEN 16 /* Extended Flow Spec */ +#define L2CAP_CFG_EXT_WIN_SIZE_LEN 2 /* Ext window size length */ +#define L2CAP_CFG_OPTION_OVERHEAD 2 /* Type and length */ + +/* Configuration Cmd/Rsp Flags mask +*/ +#define L2CAP_CFG_FLAGS_MASK_CONT 0x0001 /* Flags mask: Continuation */ + +/* FCS Check Option values +*/ +#define L2CAP_CFG_FCS_BYPASS 0 /* Bypass the FCS in streaming or ERTM modes */ +#define L2CAP_CFG_FCS_USE 1 /* Use the FCS in streaming or ERTM modes [default] */ + +/* Default values for configuration +*/ +#define L2CAP_NO_AUTOMATIC_FLUSH 0xFFFF +#define L2CAP_NO_RETRANSMISSION 0x0001 + +#define L2CAP_DEFAULT_MTU (672) +#define L2CAP_DEFAULT_FLUSH_TO L2CAP_NO_AUTOMATIC_FLUSH +#define L2CAP_DEFAULT_SERV_TYPE 1 +#define L2CAP_DEFAULT_TOKEN_RATE 0 +#define L2CAP_DEFAULT_BUCKET_SIZE 0 +#define L2CAP_DEFAULT_PEAK_BANDWIDTH 0 +#define L2CAP_DEFAULT_LATENCY 0xFFFFFFFF +#define L2CAP_DEFAULT_DELAY 0xFFFFFFFF +#define L2CAP_DEFAULT_FCS L2CAP_CFG_FCS_USE + + +/* Define the L2CAP disconnect result codes +*/ +#define L2CAP_DISC_OK 0 +#define L2CAP_DISC_TIMEOUT 0xEEEE + +/* Define the L2CAP info resp result codes +*/ +#define L2CAP_INFO_RESP_RESULT_SUCCESS 0 +#define L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED 1 + +/* Define the info-type fields of information request & response +*/ +#define L2CAP_CONNLESS_MTU_INFO_TYPE 0x0001 +#define L2CAP_EXTENDED_FEATURES_INFO_TYPE 0x0002 /* Used in Information Req/Response */ +#define L2CAP_FIXED_CHANNELS_INFO_TYPE 0x0003 /* Used in AMP */ + +#define L2CAP_CONNLESS_MTU_INFO_SIZE 2 /* Connectionless MTU size */ +#define L2CAP_EXTENDED_FEATURES_ARRAY_SIZE 4 /* Extended features array size */ +#define L2CAP_FIXED_CHNL_ARRAY_SIZE 8 /* Fixed channel array size */ + +/* Extended features mask bits +*/ +#define L2CAP_EXTFEA_RTRANS 0x00000001 /* Retransmission Mode (Not Supported) */ +#define L2CAP_EXTFEA_FC 0x00000002 /* Flow Control Mode (Not Supported) */ +#define L2CAP_EXTFEA_QOS 0x00000004 +#define L2CAP_EXTFEA_ENH_RETRANS 0x00000008 /* Enhanced retransmission mode */ +#define L2CAP_EXTFEA_STREAM_MODE 0x00000010 /* Streaming Mode */ +#define L2CAP_EXTFEA_NO_CRC 0x00000020 /* Optional FCS (if set No FCS desired) */ +#define L2CAP_EXTFEA_EXT_FLOW_SPEC 0x00000040 /* Extended flow spec */ +#define L2CAP_EXTFEA_FIXED_CHNLS 0x00000080 /* Fixed channels */ +#define L2CAP_EXTFEA_EXT_WINDOW 0x00000100 /* Extended Window Size */ +#define L2CAP_EXTFEA_UCD_RECEPTION 0x00000200 /* Unicast Connectionless Data Reception */ + +/* Mask for locally supported features used in Information Response (default to none) */ +#ifndef L2CAP_EXTFEA_SUPPORTED_MASK +#define L2CAP_EXTFEA_SUPPORTED_MASK 0 +#endif + +/* Mask for LE supported features used in Information Response (default to none) */ +#ifndef L2CAP_BLE_EXTFEA_MASK +#define L2CAP_BLE_EXTFEA_MASK 0 +#endif + +/* Define a value that tells L2CAP to use the default HCI ACL buffer pool */ +#define L2CAP_DEFAULT_ERM_POOL_ID 0xFF +/* Define a value that tells L2CAP to use the default MPS */ +#define L2CAP_DEFAULT_ERM_MPS 0x0000 + +#define L2CAP_FCR_OVERHEAD 2 /* Control word */ +#define L2CAP_FCS_LEN 2 /* FCS takes 2 bytes */ +#define L2CAP_SDU_LEN_OVERHEAD 2 /* SDU length field is 2 bytes */ +#define L2CAP_SDU_LEN_OFFSET 2 /* SDU length offset is 2 bytes */ +#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) */ +/* 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)) + +/* SAR bits in the control word +*/ +#define L2CAP_FCR_UNSEG_SDU 0x0000 /* Control word to begin with for unsegmented PDU*/ +#define L2CAP_FCR_START_SDU 0x4000 /* ...for Starting PDU of a semented SDU */ +#define L2CAP_FCR_END_SDU 0x8000 /* ...for ending PDU of a segmented SDU */ +#define L2CAP_FCR_CONT_SDU 0xc000 /* ...for continuation PDU of a segmented SDU */ + +/* Supervisory frame types +*/ +#define L2CAP_FCR_SUP_RR 0x0000 /* Supervisory frame - RR */ +#define L2CAP_FCR_SUP_REJ 0x0001 /* Supervisory frame - REJ */ +#define L2CAP_FCR_SUP_RNR 0x0002 /* Supervisory frame - RNR */ +#define L2CAP_FCR_SUP_SREJ 0x0003 /* Supervisory frame - SREJ */ + +#define L2CAP_FCR_SAR_BITS 0xC000 /* Mask to get the SAR bits from control word */ +#define L2CAP_FCR_SAR_BITS_SHIFT 14 /* Bits to shift right to get the SAR bits from ctrl-word */ + +#define L2CAP_FCR_S_FRAME_BIT 0x0001 /* Mask to check if a PDU is S-frame */ +#define L2CAP_FCR_REQ_SEQ_BITS 0x3F00 /* Mask to get the req-seq from control word */ +#define L2CAP_FCR_REQ_SEQ_BITS_SHIFT 8 /* Bits to shift right to get the req-seq from ctrl-word */ +#define L2CAP_FCR_TX_SEQ_BITS 0x007E /* Mask on get the tx-seq from control word */ +#define L2CAP_FCR_TX_SEQ_BITS_SHIFT 1 /* Bits to shift right to get the tx-seq from ctrl-word */ + +#define L2CAP_FCR_F_BIT 0x0080 /* F-bit in the control word (Sup and I frames) */ +#define L2CAP_FCR_P_BIT 0x0010 /* P-bit in the control word (Sup frames only) */ + +#define L2CAP_FCR_F_BIT_SHIFT 7 +#define L2CAP_FCR_P_BIT_SHIFT 4 + +#define L2CAP_FCR_SEG_BITS 0xC000 /* Mask to get the segmentation bits from ctrl-word */ +#define L2CAP_FCR_SUP_SHIFT 2 /* Bits to shift right to get the S-bits from ctrl-word */ +#define L2CAP_FCR_SUP_BITS 0x000C /* Mask to get the supervisory bits from ctrl-word */ + +#define L2CAP_FCR_INIT_CRC 0 /* Initial state of the CRC register */ +#define L2CAP_FCR_SEQ_MODULO 0x3F /* Mask for sequence numbers (range 0 - 63) */ + +/************************************************************************************************* +** The following definitions are only used for internal testing of ERTM at the application level +*************************************************************************************************/ +/* L2CA_SetupErtmTest() corruption test types */ +#define L2CAP_FCR_TTYPE_CORR_IFRAME 0 /* Corrupt one or more I-frames, based on count */ +#define L2CAP_FCR_TTYPE_CORR_SFRAME 1 /* Corrupt an S-frame, (acknowledgement) */ +#define L2CAP_FCR_TTYPE_STOP_TEST 2 /* Used when turning off a test */ +#define L2CAP_FCR_TTYPE_GET_CID 3 /* Returns RFCOMM cid when '0' is passed in cid argument */ + +/* L2CA_SetupErtmTest() Freq */ +#define L2CAP_FCR_FREQ_NORMAL 0 /* A single test is run */ +#define L2CAP_FCR_FREQ_RANDOM 1 /* Randomly loses or corrupts a packet */ + +#endif diff --git a/stack/include/mca_api.h b/stack/include/mca_api.h new file mode 100644 index 0000000..d455e5b --- /dev/null +++ b/stack/include/mca_api.h @@ -0,0 +1,495 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 interface file contains the interface to the Multi-Channel + * Adaptation Protocol (MCAP). + * + ******************************************************************************/ +#ifndef MCA_API_H +#define MCA_API_H + +#include "bt_target.h" +#include "l2c_api.h" + +/* move the following to bt_target.h or other place later */ +#define MCA_NUM_TC_TBL ((MCA_NUM_REGS)*(MCA_NUM_LINKS)*(MCA_NUM_MDLS+1)) +#define MCA_NUM_CCBS ((MCA_NUM_REGS)*(MCA_NUM_LINKS)) /* Number of control channel control blocks */ +#define MCA_NUM_DCBS ((MCA_NUM_REGS)*(MCA_NUM_LINKS)*(MCA_NUM_MDLS)) /* Number of data channel control blocks */ + + +/***************************************************************************** +** constants +*****************************************************************************/ +/* API function return value result codes. */ +#define MCA_SUCCESS 0 /* Function successful */ +#define MCA_BAD_PARAMS 1 /* Invalid parameters */ +#define MCA_NO_RESOURCES 2 /* Not enough resources */ +#define MCA_BAD_HANDLE 3 /* Bad handle */ +#define MCA_BUSY 4 /* A procedure is already in progress */ +#define MCA_WRITE_FAIL 5 /* Write failed */ +#define MCA_BAD_MDL_ID 6 /* MDL ID is not valid for the current API */ +typedef UINT8 tMCA_RESULT; + +/* MDEP data type. */ +#define MCA_TDEP_ECHO 0 /* MDEP for echo test */ +#define MCA_TDEP_DATA 1 /* MDEP for normal data */ + +/* Control callback events. */ +#define MCA_ERROR_RSP_EVT 0 /* error response */ +#define MCA_CREATE_IND_EVT 1 /* create mdl indication */ +#define MCA_CREATE_CFM_EVT 2 /* create mdl confirm */ +#define MCA_RECONNECT_IND_EVT 3 /* reconnect mdl indication */ +#define MCA_RECONNECT_CFM_EVT 4 /* reconnect mdl confirm */ +#define MCA_ABORT_IND_EVT 5 /* abort mdl indication */ +#define MCA_ABORT_CFM_EVT 6 /* abort mdl confirm */ +#define MCA_DELETE_IND_EVT 7 /* delete mdl indication */ +#define MCA_DELETE_CFM_EVT 8 /* delete mdl confirm */ + +#define MCA_SYNC_CAP_IND_EVT 0x11 /* request sync capabilities & requirements */ +#define MCA_SYNC_CAP_CFM_EVT 0x12 /* indicate completion */ +#define MCA_SYNC_SET_IND_EVT 0x13 /* request to set the time-stamp clock */ +#define MCA_SYNC_SET_CFM_EVT 0x14 /* indicate completion */ +#define MCA_SYNC_INFO_IND_EVT 0x15 /* update of the actual time-stamp clock instant from the sync slave */ + +#define MCA_CONNECT_IND_EVT 0x20 /* Control channel connected */ +#define MCA_DISCONNECT_IND_EVT 0x21 /* Control channel disconnected */ +#define MCA_OPEN_IND_EVT 0x22 /* Data channel open indication */ +#define MCA_OPEN_CFM_EVT 0x23 /* Data channel open confirm */ +#define MCA_CLOSE_IND_EVT 0x24 /* Data channel close indication */ +#define MCA_CLOSE_CFM_EVT 0x25 /* Data channel close confirm */ +#define MCA_CONG_CHG_EVT 0x26 /* congestion change event */ +#define MCA_RSP_TOUT_IND_EVT 0x27 /* Control channel message response timeout */ +/***************************************************************************** +** Type Definitions +*****************************************************************************/ +typedef UINT8 tMCA_HANDLE; /* the handle for registration. 1 based index to rcb */ +typedef UINT8 tMCA_CL; /* the handle for a control channel; reported at MCA_CONNECT_IND_EVT */ +typedef UINT8 tMCA_DEP; /* the handle for MCA_CreateDep. This is also the local mdep_id */ +typedef UINT16 tMCA_DL; /* the handle for the data channel. This is reported at MCA_OPEN_CFM_EVT or MCA_OPEN_IND_EVT */ + +/* This is the data callback function. It is executed when MCAP has a data +** packet ready for the application. +*/ +typedef void (tMCA_DATA_CBACK)(tMCA_DL mdl, BT_HDR *p_pkt); + + +/* This structure contains parameters which are set at registration. */ +typedef struct { + UINT32 rsp_tout; /* MCAP signaling response timeout */ + UINT16 ctrl_psm; /* L2CAP PSM for the MCAP control channel */ + UINT16 data_psm; /* L2CAP PSM for the MCAP data channel */ + UINT16 sec_mask; /* Security mask for BTM_SetSecurityLevel() */ +} tMCA_REG; + +/* This structure contains parameters to create a MDEP. */ +typedef struct { + UINT8 type; /* MCA_TDEP_DATA, or MCA_TDEP_ECHO. a regiatration may have only one MCA_TDEP_ECHO MDEP */ + UINT8 max_mdl; /* The maximum number of MDLs for this MDEP (max is MCA_NUM_MDLS) */ + tMCA_DATA_CBACK *p_data_cback; /* Data callback function */ +} tMCA_CS; + +#define MCA_FCS_NONE 0 /* fcs_present=FALSE */ +#define MCA_FCS_BYPASS 0x10 /* fcs_present=TRUE, fcs=L2CAP_CFG_FCS_BYPASS */ +#define MCA_FCS_USE 0x11 /* fcs_present=TRUE, fcs=L2CAP_CFG_FCS_USE */ +#define MCA_FCS_PRESNT_MASK 0x10 /* fcs_present=TRUE */ +#define MCA_FCS_USE_MASK 0x01 /* mask for fcs */ +typedef UINT8 tMCA_FCS_OPT; + +/* This structure contains L2CAP configuration parameters for the channel. */ +typedef struct { + tL2CAP_FCR_OPTS fcr_opt; + UINT8 user_rx_pool_id; + UINT8 user_tx_pool_id; + UINT8 fcr_rx_pool_id; + UINT8 fcr_tx_pool_id; + tMCA_FCS_OPT fcs; + UINT16 data_mtu; /* L2CAP MTU of the MCAP data channel */ +} tMCA_CHNL_CFG; + + +/* Header structure for callback event parameters. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + UINT8 op_code; /* The op (request/response) code */ +} tMCA_EVT_HDR; + +/* Response Header structure for callback event parameters. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + UINT8 op_code; /* The op (request/response) code */ + UINT8 rsp_code; /* The response code */ +} tMCA_RSP_EVT; + +/* This data structure is associated with the MCA_CREATE_IND_EVT. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + UINT8 op_code; /* The op (request/response) code */ + UINT8 dep_id; /* MDEP ID */ + UINT8 cfg; /* The configuration to negotiate */ +} tMCA_CREATE_IND; + +/* This data structure is associated with the MCA_CREATE_CFM_EVT. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + UINT8 op_code; /* The op (request/response) code */ + UINT8 rsp_code; /* The response code. */ + UINT8 cfg; /* The configuration to negotiate */ +} tMCA_CREATE_CFM; + +/* This data structure is associated with MCA_CONNECT_IND_EVT. */ +typedef struct { + BD_ADDR bd_addr; /* The peer address */ + UINT16 mtu; /* peer mtu */ +} tMCA_CONNECT_IND; + +/* This data structure is associated with MCA_DISCONNECT_IND_EVT. */ +typedef struct { + BD_ADDR bd_addr; /* The peer address */ + UINT16 reason; /* disconnect reason given by L2CAP */ +} tMCA_DISCONNECT_IND; + +/* This data structure is associated with MCA_OPEN_IND_EVT, and MCA_OPEN_CFM_EVT. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + tMCA_DL mdl; /* The handle for the data channel */ + UINT16 mtu; /* peer mtu */ +} tMCA_DL_OPEN; + +/* This data structure is associated with MCA_CLOSE_IND_EVT and MCA_CLOSE_CFM_EVT. */ +typedef struct { + UINT16 mdl_id; /* The associated MDL ID */ + tMCA_DL mdl; /* The handle for the data channel */ + UINT16 reason; /* disconnect reason given by L2CAP */ +} tMCA_DL_CLOSE; + +/* This data structure is associated with MCA_CONG_CHG_EVT. */ +typedef struct { + UINT16 mdl_id; /* N/A - This is a place holder */ + tMCA_DL mdl; /* The handle for the data channel */ + BOOLEAN cong; /* TRUE, if the channel is congested */ +} tMCA_CONG_CHG; + +/* Union of all control callback event data structures */ +typedef union { + tMCA_EVT_HDR hdr; + tMCA_RSP_EVT rsp; + tMCA_CREATE_IND create_ind; + tMCA_CREATE_CFM create_cfm; + tMCA_EVT_HDR reconnect_ind; + tMCA_RSP_EVT reconnect_cfm; + tMCA_EVT_HDR abort_ind; + tMCA_RSP_EVT abort_cfm; + tMCA_EVT_HDR delete_ind; + tMCA_RSP_EVT delete_cfm; + tMCA_CONNECT_IND connect_ind; + tMCA_DISCONNECT_IND disconnect_ind; + tMCA_DL_OPEN open_ind; + tMCA_DL_OPEN open_cfm; + tMCA_DL_CLOSE close_ind; + tMCA_DL_CLOSE close_cfm; + tMCA_CONG_CHG cong_chg; +} tMCA_CTRL; + +/* This is the control callback function. This function passes control events +** to the application. +*/ +typedef void (tMCA_CTRL_CBACK)(tMCA_HANDLE handle, tMCA_CL mcl, UINT8 event, + tMCA_CTRL *p_data); + + +/******************************************************************************* +** +** Function MCA_Init +** +** Description Initialize MCAP internal control blocks. +** This function is called at stack start up. +** +** Returns void +** +*******************************************************************************/ +MCA_API extern void MCA_Init(void); + +/******************************************************************************* +** +** Function MCA_SetTraceLevel +** +** Description This function sets the debug trace level for MCA. +** If 0xff is passed, the current trace level is returned. +** +** Input Parameters: +** level: The level to set the MCA tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +*******************************************************************************/ +MCA_API extern UINT8 MCA_SetTraceLevel (UINT8 level); + +/******************************************************************************* +** +** Function MCA_Register +** +** Description This function registers an MCAP implementation. +** It is assumed that the control channel PSM and data channel +** PSM are not used by any other instances of the stack. +** If the given p_reg->ctrl_psm is 0, this handle is INT only. +** +** Returns 0, if failed. Otherwise, the MCA handle. +** +*******************************************************************************/ +MCA_API extern tMCA_HANDLE MCA_Register(tMCA_REG *p_reg, tMCA_CTRL_CBACK *p_cback); + +/******************************************************************************* +** +** Function MCA_Deregister +** +** Description This function is called to deregister an MCAP implementation. +** Before this function can be called, all control and data +** channels must be removed with MCA_DisconnectReq and MCA_CloseReq. +** +** Returns void +** +*******************************************************************************/ +MCA_API extern void MCA_Deregister(tMCA_HANDLE handle); + +/******************************************************************************* +** +** Function MCA_CreateDep +** +** Description Create a data endpoint. If the MDEP is created successfully, +** the MDEP ID is returned in *p_dep. After a data endpoint is +** created, an application can initiate a connection between this +** endpoint and an endpoint on a peer device. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_CreateDep(tMCA_HANDLE handle, tMCA_DEP *p_dep, tMCA_CS *p_cs); + +/******************************************************************************* +** +** Function MCA_DeleteDep +** +** Description Delete a data endpoint. This function is called when +** the implementation is no longer using a data endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and the data endpoint +** is removed. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_DeleteDep(tMCA_HANDLE handle, tMCA_DEP dep); + +/******************************************************************************* +** +** Function MCA_ConnectReq +** +** Description This function initiates an MCAP control channel connection +** to the peer device. When the connection is completed, an +** MCA_CONNECT_IND_EVT is reported to the application via its +** control callback function. +** This control channel is identified by tMCA_CL. +** If the connection attempt fails, an MCA_DISCONNECT_IND_EVT is +** reported. The security mask parameter overrides the outgoing +** security mask set in MCA_Register(). +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_ConnectReq(tMCA_HANDLE handle, BD_ADDR bd_addr, + UINT16 ctrl_psm, + UINT16 sec_mask); + +/******************************************************************************* +** +** Function MCA_DisconnectReq +** +** Description This function disconnect an MCAP control channel +** to the peer device. +** If associated data channel exists, they are disconnected. +** When the MCL is disconnected an MCA_DISCONNECT_IND_EVT is +** reported to the application via its control callback function. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_DisconnectReq(tMCA_CL mcl); + +/******************************************************************************* +** +** Function MCA_CreateMdl +** +** Description This function sends a CREATE_MDL request to the peer device. +** When the response is received, a MCA_CREATE_CFM_EVT is reported +** with the given MDL ID. +** If the response is successful, a data channel is open +** with the given p_chnl_cfg +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_CreateMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm, + UINT16 mdl_id, UINT8 peer_dep_id, + UINT8 cfg, const tMCA_CHNL_CFG *p_chnl_cfg); + +/******************************************************************************* +** +** Function MCA_CreateMdlRsp +** +** Description This function sends a CREATE_MDL response to the peer device +** in response to a received MCA_CREATE_IND_EVT. +** If the rsp_code is successful, a data channel is open +** with the given p_chnl_cfg +** When the data channel is open successfully, a MCA_OPEN_IND_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_CreateMdlRsp(tMCA_CL mcl, tMCA_DEP dep, + UINT16 mdl_id, UINT8 cfg, UINT8 rsp_code, + const tMCA_CHNL_CFG *p_chnl_cfg); + +/******************************************************************************* +** +** Function MCA_CloseReq +** +** Description Close a data channel. When the channel is closed, an +** MCA_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_CloseReq(tMCA_DL mdl); + +/******************************************************************************* +** +** Function MCA_ReconnectMdl +** +** Description This function sends a RECONNECT_MDL request to the peer device. +** When the response is received, a MCA_RECONNECT_CFM_EVT is reported. +** If the response is successful, a data channel is open. +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_ReconnectMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm, + UINT16 mdl_id, const tMCA_CHNL_CFG *p_chnl_cfg); + +/******************************************************************************* +** +** Function MCA_ReconnectMdlRsp +** +** Description This function sends a RECONNECT_MDL response to the peer device +** in response to a MCA_RECONNECT_IND_EVT event. +** If the response is successful, a data channel is open. +** When the data channel is open successfully, a MCA_OPEN_IND_EVT +** is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_ReconnectMdlRsp(tMCA_CL mcl, tMCA_DEP dep, + UINT16 mdl_id, UINT8 rsp_code, + const tMCA_CHNL_CFG *p_chnl_cfg); + +/******************************************************************************* +** +** Function MCA_DataChnlCfg +** +** Description This function initiates a data channel connection toward the +** connected peer device. +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_DataChnlCfg(tMCA_CL mcl, const tMCA_CHNL_CFG *p_chnl_cfg); + +/******************************************************************************* +** +** Function MCA_Abort +** +** Description This function sends a ABORT_MDL request to the peer device. +** When the response is received, a MCA_ABORT_CFM_EVT is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_Abort(tMCA_CL mcl); + +/******************************************************************************* +** +** Function MCA_Delete +** +** Description This function sends a DELETE_MDL request to the peer device. +** When the response is received, a MCA_DELETE_CFM_EVT is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_Delete(tMCA_CL mcl, UINT16 mdl_id); + +/******************************************************************************* +** +** Function MCA_WriteReq +** +** Description Send a data packet to the peer device. +** +** The application passes the packet using the BT_HDR structure. +** The offset field must be equal to or greater than L2CAP_MIN_OFFSET. +** This allows enough space in the buffer for the L2CAP header. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +MCA_API extern tMCA_RESULT MCA_WriteReq(tMCA_DL mdl, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function MCA_GetL2CapChannel +** +** Description Get the L2CAP CID used by the given data channel handle. +** +** Returns L2CAP channel ID if successful, otherwise 0. +** +*******************************************************************************/ +MCA_API extern UINT16 MCA_GetL2CapChannel (tMCA_DL mdl); + +#endif /* MCA_API_H */ diff --git a/stack/include/mca_defs.h b/stack/include/mca_defs.h new file mode 100644 index 0000000..cca7fe0 --- /dev/null +++ b/stack/include/mca_defs.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 contains constants definitions and other information from the MCAP + * specification. + * + ******************************************************************************/ +#ifndef MCA_DEFS_H +#define MCA_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ +#define MCA_MIN_MTU 48 + +/* standard op codes */ +#define MCA_OP_ERROR_RSP 0x00 /* invalid opcode response */ +#define MCA_OP_MDL_CREATE_REQ 0x01 /* create an MDL, wait for an associated data channel connection */ +#define MCA_OP_MDL_CREATE_RSP 0x02 /* response to above request */ +#define MCA_OP_MDL_RECONNECT_REQ 0x03 /* req to prepare to rvc a data channel conn associated with a prev MDL */ +#define MCA_OP_MDL_RECONNECT_RSP 0x04 /* response to above request */ +#define MCA_OP_MDL_ABORT_REQ 0x05 /* stop waiting for a data channel connection */ +#define MCA_OP_MDL_ABORT_RSP 0x06 /* response to above request */ +#define MCA_OP_MDL_DELETE_REQ 0x07 /* delete an MDL */ +#define MCA_OP_MDL_DELETE_RSP 0x08 /* response to above request */ +#define MCA_NUM_STANDARD_OPCODE (1+MCA_OP_MDL_DELETE_RSP) + +/* clock synchronization op codes */ +#define MCA_OP_SYNC_CAP_REQ 0x11 /* request sync capabilities & requirements */ +#define MCA_OP_SYNC_CAP_RSP 0x12 /* indicate completion */ +#define MCA_OP_SYNC_SET_REQ 0x13 /* request to set the time-stamp clock */ +#define MCA_OP_SYNC_SET_RSP 0x14 /* indicate completion */ +#define MCA_OP_SYNC_INFO_IND 0x15 /* update of the actual time-stamp clock instant from the sync slave */ + +#define MCA_FIRST_SYNC_OP MCA_OP_SYNC_CAP_REQ +#define MCA_LAST_SYNC_OP MCA_OP_SYNC_INFO_IND + +/* response code */ +#define MCA_RSP_SUCCESS 0x00 /* The corresponding request was received and processed successfully. */ +#define MCA_RSP_BAD_OPCODE 0x01 /* The Op Code received is not valid (i.e. neither a Standard Op Code nor a Clock Synchronization Protocol Op Code). */ +#define MCA_RSP_BAD_PARAM 0x02 /* One or more of the values in the received request is invalid. */ +/* MCA_RSP_BAD_PARAM shall be used when: +- The request length is invalid +- Some of the parameters have invalid values and none of the other defined Response Codes are more appropriate. +*/ +#define MCA_RSP_BAD_MDEP 0x03 /* The MDEP ID referenced does not exist on this device. */ +#define MCA_RSP_MDEP_BUSY 0x04 /* The requested MDEP currently has as many active MDLs as it can manage simultaneously. */ +#define MCA_RSP_BAD_MDL 0x05 /* The MDL ID referenced is invalid. */ +/* MCA_RSP_BAD_MDL shall be used when: +- A reserved or invalid value for MDL ID was used. +- The MDL ID referenced is not available (was never created, has been deleted, or was otherwise lost), +- The MDL ID referenced in the Abort request is not the same value that was used to initiate the PENDING state +*/ +#define MCA_RSP_MDL_BUSY 0x06 /* The device is temporarily unable to complete the request. This is intended for reasons not related to the physical sensor (e.g. communication resources unavailable). */ +#define MCA_RSP_BAD_OP 0x07 /* The received request is invalid in the current state. */ +/* MCA_RSP_BAD_OP is used when +- Abort request was received while not in the PENDING state. +- Create, Reconnect, or Delete request was received while in the PENDING state. +- A response is received when a request is expected +*/ +#define MCA_RSP_NO_RESOURCE 0x08 /* The device is temporarily unable to complete the request. This is intended for reasons relating to the physical sensor (e.g. hardware fault, low battery), or when processing resources are temporarily committed to other processes. */ +#define MCA_RSP_ERROR 0x09 /* An internal error other than those listed in this table was encountered while processing the request. */ +#define MCA_RSP_NO_SUPPORT 0x0A /* The Op Code that was used in this request is not supported. */ +#define MCA_RSP_CFG_REJ 0x0B /* A configuration required by a MD_CREATE_MDL or MD_RECONNECT_MDL operation has been rejected. */ + +#define MCA_MAX_MDEP_ID 0x7F /* the valid range for MDEP ID is 1-0x7F */ +#define MCA_IS_VALID_MDL_ID(xxx) (((xxx)>0) && ((xxx)<=0xFEFF)) +#define MCA_ALL_MDL_ID 0xFFFF + +#endif /* MCA_DEFS_H */ diff --git a/stack/include/obx_api.h b/stack/include/obx_api.h new file mode 100644 index 0000000..472eeb0 --- /dev/null +++ b/stack/include/obx_api.h @@ -0,0 +1,1704 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ +#ifndef OBX_API_H +#define OBX_API_H +#include "bt_target.h" +#include "bt_types.h" +#include "l2c_api.h" + +/* 18 is 7/OBX_CONN_HDRS_OFFSET + 5/conn id, 2/ssn, 2/srm 2/srm_param */ +#define OBX_HDR_OFFSET (18 + L2CAP_MIN_OFFSET) +#define OBX_MAX_TRIPLET 3 + +#define OBX_MIN_MTU 255 /* per IrOBEX spec */ +#define OBX_MAX_MTU (OBX_LRG_DATA_POOL_SIZE - BT_HDR_SIZE - OBX_HDR_OFFSET) + +#define OBX_CONN_ID_SIZE 4 +#define OBX_PKT_LEN_SIZE 2 + +#define OBX_CONN_HDRS_OFFSET 7 +#define OBX_SESS_HDRS_OFFSET 3 +#define OBX_DISCON_HDRS_OFFSET 3 +#define OBX_PUT_HDRS_OFFSET 3 +#define OBX_GET_HDRS_OFFSET 3 +#define OBX_SETPATH_REQ_HDRS_OFFSET 5 +#define OBX_ABORT_HDRS_OFFSET 3 +#define OBX_ACTION_HDRS_OFFSET 3 +#define OBX_RESPONSE_HDRS_OFFSET 3 + +/* this is not needed if OBX_HDR_OFFSET is 18+ */ +#define OBX_MAX_CONN_HDR_EXTRA 8 /* 5/conn id, 2/ssn, 2/srm 2/srm_param - (5/setpath + 5/conn_id - 7/conn) */ + +/* offset for header functions to access fields */ +#define OBX_CONNECT_MTU_OFFSET 5 +#define OBX_SETPATH_FLAG_OFFSET 3 + +#define OBX_MAX_NUM_AUTH_TRIPLET 3 +#define OBX_UNICODE_SIZE 2 /* sizeof(UINT16) */ + +#define OBX_INVALID_HDR_LEN 0xFFFF + +#define OBX_MIN_NONCE_SIZE 4 /* fixed size per IrOBEX spec */ +#define OBX_NONCE_SIZE 16 /* fixed size per IrOBEX spec */ +#define OBX_DIGEST_SIZE 16 /* fixed size per IrOBEX spec */ +#define OBX_MAX_AUTH_KEY_SIZE 16 +#define OBX_MAX_AUTH_USER_SIZE 20 +#define OBX_SESSION_ID_SIZE 16 /* fixed size per IrOBEX spec */ +#define OBX_SESSION_INFO_SIZE 32 /* OBX_SESSION_ID_SIZE + 4(local nonce) + 4 (connection id) + 4 (timeout) + 2(mtu) + 1(state) + 1(srm) */ +#define OBX_SESSION_INFO_NONCE_IDX 16 /* The index to the (local nonce) in session info */ +#define OBX_SESSION_INFO_ID_IDX 20 /* The index to the (connection id) in session info */ +#define OBX_SESSION_INFO_TO_IDX 24 /* The index to the (timeout) in session info */ +#define OBX_SESSION_INFO_MTU_IDX 28 /* The index to peer MTU in session info */ +#define OBX_SESSION_INFO_ST_IDX 30 /* The index to sr/cl state in session info */ +#define OBX_SESSION_INFO_SRM_IDX 31 /* The index to srm in session info */ +#define OBX_TIMEOUT_SIZE 4 + +/* handle related definitions */ +#define OBX_SESS_SHIFT 8 +#define OBX_ENC_SESS_HANDLE(oh, os) (((os)<>OBX_SESS_SHIFT) + +/* Return values for API functions */ +enum +{ + OBX_SUCCESS, /* Status is successful. */ + OBX_BAD_PARAMS, /* Bad parameter(s). */ + OBX_NO_RESOURCES, /* No resources (GKI buffers, control block) */ + OBX_BAD_HANDLE /* The OBEX handle is not valid. */ +}; +typedef UINT8 tOBX_STATUS; + + +typedef UINT16 tOBX_HANDLE; + +#define OBX_HANDLE_NULL 0 + +enum +{ + OBX_PT_PUT, /* Regular Put request */ + OBX_PT_DELETE, /* Delete request - a Put request with NO Body or End-of-Body header. */ + OBX_PT_CREATE /* Create-Empty request - a Put request with an empty End-of-Body header. */ +}; +typedef UINT8 tOBX_PUT_TYPE; + +/* SetPath Request Flags - the following definitions can be ORed if both flags are wanted */ +#define OBX_SPF_BACKUP 0x01 /* Backup a level before applying name(equivalent to ../) */ +#define OBX_SPF_NO_CREATE 0x02 /* Don't create directory if it does not exist, return an error instead. */ +typedef UINT8 tOBX_SETPATH_FLAG; + +/* Authentication Challenge Options */ +#define OBX_AO_NONE 0x00 /* If this is used in OBX_StartServer and the authenticate + * flag is TRUE, the optional Challenge Information (tag 0x01) + * will not be sent. */ +#define OBX_AO_USR_ID 0x01 /* Set this bit to make the client respond with the user ID. */ +typedef UINT8 tOBX_AUTH_OPT; + +/* CHARSET definition for Authentication Challenge Realm */ +#define OBX_RCS_ASCII 0x00 /* ASCII */ +#define OBX_RCS_8859_1 0x01 /* ISO-8859-1 */ +#define OBX_RCS_8859_2 0x02 /* ISO-8859-2 */ +#define OBX_RCS_8859_3 0x03 /* ISO-8859-3 */ +#define OBX_RCS_8859_4 0x04 /* ISO-8859-4 */ +#define OBX_RCS_8859_5 0x05 /* ISO-8859-5 */ +#define OBX_RCS_8859_6 0x06 /* ISO-8859-6 */ +#define OBX_RCS_8859_7 0x07 /* ISO-8859-7 */ +#define OBX_RCS_8859_8 0x08 /* ISO-8859-8 */ +#define OBX_RCS_8859_9 0x09 /* ISO-8859-9 */ +#define OBX_RCS_UNICODE 0xFF /* Unicode */ +typedef UINT8 tOBX_CHARSET; + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + BOOLEAN final; /* TRUE, if this is the final packet of this PUT transaction. */ + tOBX_PUT_TYPE type; /* The type of PUT request. */ +} tOBX_PUT_EVT; + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + BOOLEAN final; /* TRUE, if this is the final packet of this GET transaction. */ +} tOBX_GET_EVT; + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + BD_ADDR peer_addr; /* The peer Bluetooth Address. */ + UINT16 mtu; /* The peer MTU. This element is associated with OBX_CONNECT_REQ_EVT and OBX_CONNECT_RSP_EVT. */ + tOBX_HANDLE handle; /* the OBX handle returned by OBX_StartServer(), OBX_CreateSession() and OBX_ConnectReq() */ + BOOLEAN no_rsp; /* TRUE, when the event is generated as a part of RESUME SESSION */ +} tOBX_CONN_EVT; + +/* Session Opcode Definitions: */ +#define OBX_SESS_OP_CREATE 0x00 /* Create Session */ +#define OBX_SESS_OP_CLOSE 0x01 /* Close Session */ +#define OBX_SESS_OP_SUSPEND 0x02 /* Suspend Session */ +#define OBX_SESS_OP_RESUME 0x03 /* Resume Session */ +#define OBX_SESS_OP_SET_TIME 0x04 /* Set Timeout */ +#define OBX_SESS_OP_TRANSPORT 0xFF /* transport dropped */ +typedef UINT8 tOBX_SESS_OP; + +/* Session States Definitions for external use: */ +enum +{ + OBX_SESS_NONE, /* 0x00 session is not engaged/closed */ + OBX_SESS_ACTIVE, /* 0x01 session is active. */ + OBX_SESS_SUSPENDED, /* 0x02 session is suspended. */ + OBX_SESS_EXT_MAX +}; +typedef UINT8 tOBX_SESS_ST; + + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + tOBX_SESS_OP sess_op; /* the session op code */ + tOBX_SESS_ST sess_st; /* the session state */ + BD_ADDR peer_addr; /* The peer Bluetooth Address. */ + UINT8 *p_sess_info; /* The session ID and the local nonce for a reliable session, a reference to the location in OBEX control block or NULL */ + UINT32 timeout; /* The number of seconds remaining in suspend. 0xffff if infinite. */ + UINT32 obj_offset; /* The object offset for resume session. */ + UINT8 nssn; /* tne next session sequence number the server expects */ +} tOBX_SESS_EVT; + +#define OBX_ACT_COPY 0x00 /* Copy object */ +#define OBX_ACT_MOVE 0x01 /* Move/rename object */ +#define OBX_ACT_PERMISSION 0x02 /* Set object permission */ +typedef UINT8 tOBX_ACTION; + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + tOBX_ACTION action; /* The action opcode. */ +} tOBX_ACT_EVT; + +typedef struct +{ + UINT8 ssn; /* session sequence number */ + tOBX_SETPATH_FLAG flag; /* The set path flags. */ +} tOBX_SETPATH_EVT; + +/* permission flags */ +#define OBX_PERMISSION_READ 0x01 +#define OBX_PERMISSION_WRITE 0x02 +#define OBX_PERMISSION_DELETE 0x04 +#define OBX_PERMISSION_MODIFY 0x80 + +typedef union +{ + UINT8 ssn; /* session sequence number */ + tOBX_CONN_EVT conn; /* This element is associated with OBX_CONNECT_REQ_EVT and OBX_CONNECT_RSP_EVT. */ + tOBX_SESS_EVT sess; /* This element is associated with OBX_SESSION_RSP_EVT and OBX_SESSION_REQ_EVT. */ + tOBX_PUT_EVT put; /* This element is associated with OBX_PUT_REQ_EVT. */ + tOBX_SETPATH_EVT sp; /* This element is associated with OBX_SETPATH_REQ_EVT. */ + tOBX_ACT_EVT action; /* This element is associated with OBX_ACTION_REQ_EVT */ + tOBX_GET_EVT get; /* This element is associated with OBX_GET_REQ_EVT. TRUE, if this is the final packet that contains the OBEX headers for this GET request. */ +} tOBX_EVT_PARAM; + + +enum +{ + OBX_NULL_EVT, + /* server events */ + OBX_CONNECT_REQ_EVT, /* param = packet MTU */ + OBX_SESSION_REQ_EVT, /* A Crease Session or Resume Session request is received by the server. Call OBX_SessionRsp(). */ + OBX_DISCONNECT_REQ_EVT, + OBX_PUT_REQ_EVT, /* could have param indicate delete or create? */ + OBX_GET_REQ_EVT, + OBX_SETPATH_REQ_EVT, /* param = flags */ + OBX_ABORT_REQ_EVT, + OBX_ACTION_REQ_EVT, /* An Action request is received by the server. Call OBX_ActionRsp(). */ + + /* client events */ + OBX_CONNECT_RSP_EVT, /* param = packet MTU */ + OBX_SESSION_RSP_EVT, /* A response for Create Session or Resume Session is received by the client. The client needs to remember the session id. The session id is to be used in calling OBX_ResumeSession, if the current session is terminated prematurely. */ + OBX_DISCONNECT_RSP_EVT, + OBX_PUT_RSP_EVT, + OBX_GET_RSP_EVT, + OBX_SETPATH_RSP_EVT, + OBX_ABORT_RSP_EVT, + OBX_ACTION_RSP_EVT, /* An Action response is received by the client. */ + + /* common events */ + OBX_SESSION_INFO_EVT, /* the session information event to resume the session. */ + OBX_CLOSE_IND_EVT, /* when transport goes down; p_pkt = NULL; no response needed */ + OBX_TIMEOUT_EVT, /* param = tOBX_EVENT */ + OBX_PASSWORD_EVT +}; +typedef UINT8 tOBX_EVENT; + +/* this is used by the protocol display function only*/ +enum +{ + OBX_NULL_DISP, + /* server events */ + OBX_CONNECT_REQ_DISP, + OBX_SESSION_REQ_DISP, + OBX_DISCONNECT_REQ_DISP, + OBX_PUT_REQ_DISP, + OBX_GET_REQ_DISP, + OBX_SETPATH_REQ_DISP, + OBX_ABORT_REQ_DISP, + OBX_ACTION_REQ_DISP, + /* client events */ + OBX_CONNECT_RSP_DISP, + OBX_SESSION_RSP_DISP, + OBX_DISCONNECT_RSP_DISP, + OBX_PUT_RSP_DISP, + OBX_GET_RSP_DISP, + OBX_SETPATH_RSP_DISP, + OBX_ABORT_RSP_DISP, + OBX_ACTION_RSP_DISP, + /* common events */ + OBX_CLOSE_IND_DISP, + OBX_TIMEOUT_DISP, + OBX_PASSWORD_DISP +}; +#define OBX_DISP_IS_RECV 0x80 +#define OBX_DISP_TYPE_MASK 0x7F + +#define OBX_MAX_EVT OBX_PASSWORD_EVT +#define OBX_MAX_OFFSET_IND OBX_ABORT_RSP_EVT /* This is used to access obx_hdr_start_offset */ + +/* +** Define Miscellaneous Constants +*/ +#define OBX_VERSION 0x10 /* Version 1.0 */ +#define OBX_CONN_FLAGS 0 /* Connect flags per IrOBEX spec */ +#define OBX_SETPATH_CONST 0 /* SetPath Request constants per IrOBEX spec */ +#define OBX_INVALID_CONN_ID 0xFFFFFFFF /* invalid connection ID per IrOBEX spec */ +#define OBX_INFINITE_TIMEOUT 0xFFFFFFFF + +/* Header Identifier Data Type Constants */ +#define OBX_HI_TYPE_MASK 0xC0 /* This mask get the encoding (data type) of the header ID. */ +#define OBX_HI_ID_MASK 0x3F /* This mask gets the meaning of the header ID. */ +#define OBX_HI_TYPE_UNIC 0x00 /* Null terminated Unicode text */ +#define OBX_HI_TYPE_ARRAY 0x40 /* Unstructured octet array (byte sequence) */ +#define OBX_HI_TYPE_BYTE 0x80 /* 8-bit integer */ +#define OBX_HI_TYPE_INT 0xC0 /* 32-bit integer */ + +/* +** Define OBEX Header Identifiers +*/ +#define OBX_HI_NULL 0 +#define OBX_HI_COUNT 0xC0 +#define OBX_HI_NAME 0x01 +#define OBX_HI_TYPE 0x42 +#define OBX_HI_LENGTH 0xC3 +#define OBX_HI_TIME 0x44 +#define OBX_HI_DESCRIPTION 0x05 +#define OBX_HI_TARGET 0x46 +#define OBX_HI_HTTP 0x47 +#define OBX_HI_BODY 0x48 +#define OBX_HI_BODY_END 0x49 +#define OBX_HI_WHO 0x4A +#define OBX_HI_CONN_ID 0xCB +#define OBX_HI_APP_PARMS 0x4C +#define OBX_HI_CHALLENGE 0x4D +#define OBX_HI_AUTH_RSP 0x4E +#define OBX_HI_CREATOR_ID 0xCF +#define OBX_HI_WAN_UUID 0x50 +#define OBX_HI_OBJ_CLASS 0x51 +#define OBX_HI_SESSION_PARAM 0x52 +#define OBX_HI_SESSION_SN 0x93 +#define OBX_HI_ACTION_ID 0x94 +#define OBX_HI_DEST_NAME 0x15 +#define OBX_HI_PERMISSION 0xD6 +#define OBX_HI_SRM 0x97 +#define OBX_HI_SRM_PARAM 0x98 +#define OBX_HI_LO_USER 0x30 +#define OBX_HI_HI_USER 0x3F + +/* Obex Header Values for the SRM header */ +#define OBX_HV_SRM_DISABLE 0x00 /* SRM header value - disable */ +#define OBX_HV_SRM_ENABLE 0x01 /* SRM header value - enable */ +#define OBX_HV_SRM_IND 0x02 /* SRM header value - indicate support */ + +/* Obex Header Values for the SRM Parameter header */ +#define OBX_HV_SRM_PARAM_MORE 0x00 /* SRM Param header value - request additional packet */ +#define OBX_HV_SRM_PARAM_WAIT 0x01 /* SRM Param header value - wait for next req/rsp */ +#define OBX_HV_SRM_PARAM_COMBO 0x02 /* SRM Param header value - next and wait */ + +#define OBX_TAG_SESS_PARAM_ADDR 0x00 +#define OBX_TAG_SESS_PARAM_NONCE 0x01 +#define OBX_TAG_SESS_PARAM_SESS_ID 0x02 +#define OBX_TAG_SESS_PARAM_NSEQNUM 0x03 +#define OBX_TAG_SESS_PARAM_TOUT 0x04 +#define OBX_TAG_SESS_PARAM_SESS_OP 0x05 +#define OBX_TAG_SESS_PARAM_OBJ_OFF 0x06 +#define OBX_MAX_SESS_PARAM_TRIP 7 /* max number of TLV for session operations */ + +#define OBX_LEN_SESS_PARAM_SESS_OP 1 +#define OBX_LEN_SESS_PARAM_OBJ_OFF 4 /* this value varies, so it needs to be verified on the receiving side */ + +/* +** Define OBEX Request Codes +*/ +#define OBX_REQ_CONNECT 0x00 /* need to set final bit */ +#define OBX_REQ_DISCONNECT 0x01 /* need to set final bit */ +#define OBX_REQ_PUT 0x02 +#define OBX_REQ_GET 0x03 +#define OBX_REQ_SETPATH 0x05 /* need to set final bit */ +#define OBX_REQ_ACTION 0x06 +#define OBX_REQ_SESSION 0x07 /* need to set final bit */ +#define OBX_REQ_ABORT 0x7F /* need to set final bit */ +#define OBX_FINAL 0x80 + +/* OBEX response code as defined in IrOBEX spec. version 1.2 */ +#define OBX_RSP_DEFAULT 0x00 +#define OBX_RSP_FAILED 0x08 /* OBEX failed - not from spec */ +#define OBX_RSP_CONTINUE 0x10 /* Continue */ +#define OBX_RSP_OK 0x20 /* OK, Success */ +#define OBX_RSP_CREATED 0x21 /* Created */ +#define OBX_RSP_ACCEPTED 0x22 /* Accepted */ +#define OBX_RSP_NON_AUTH_INFO 0x23 /* Non-Authoritative Information */ +#define OBX_RSP_NO_CONTENT 0x24 /* No Content */ +#define OBX_RSP_RESET_CONTENT 0x25 /* Reset Content */ +#define OBX_RSP_PART_CONTENT 0x26 /* Partial Content */ +#define OBX_RSP_MULTI_CHOICES 0x30 /* Multiple Choices */ +#define OBX_RSP_MVD_PERM 0x31 /* Moved Permanently */ +#define OBX_RSP_MVD_TEMP 0x32 /* Moved temporarily */ +#define OBX_RSP_SEE_OTHER 0x33 /* See Other */ +#define OBX_RSP_NOT_MODIFIED 0x34 /* Not modified */ +#define OBX_RSP_USE_PROXY 0x35 /* Use Proxy */ +#define OBX_RSP_BAD_REQUEST 0x40 /* Bad Request - server couldn't understand request */ +#define OBX_RSP_UNAUTHORIZED 0x41 /* Unauthorized */ +#define OBX_RSP_PAYMENT_REQD 0x42 /* Payment required */ +#define OBX_RSP_FORBIDDEN 0x43 /* Forbidden - operation is understood but refused */ +#define OBX_RSP_NOT_FOUND 0x44 /* Not Found */ +#define OBX_RSP_NOT_ALLOWED 0x45 /* Method not allowed */ +#define OBX_RSP_NOT_ACCEPTABLE 0x46 /* Not Acceptable */ +#define OBX_RSP_PROXY_AUTH_REQD 0x47 /* Proxy Authentication required */ +#define OBX_RSP_REQUEST_TIMEOUT 0x48 /* Request Time Out */ +#define OBX_RSP_CONFLICT 0x49 /* Conflict */ +#define OBX_RSP_GONE 0x4A /* Gone */ +#define OBX_RSP_LENGTH_REQD 0x4B /* Length Required */ +#define OBX_RSP_PRECONDTN_FAILED 0x4C /* Precondition failed */ +#define OBX_RSP_REQ_ENT_2_LARGE 0x4D /* Requested entity too large */ +#define OBX_RSP_REQ_URL_2_LARGE 0x4E /* Request URL too large */ +#define OBX_RSP_UNSUPTD_TYPE 0x4F /* Unsupported media type */ +#define OBX_RSP_INTRNL_SRVR_ERR 0x50 /* Internal Server Error */ +#define OBX_RSP_NOT_IMPLEMENTED 0x51 /* Not Implemented */ +#define OBX_RSP_BAD_GATEWAY 0x52 /* Bad Gateway */ +#define OBX_RSP_SERVICE_UNAVL 0x53 /* Service Unavailable */ +#define OBX_RSP_GATEWAY_TIMEOUT 0x54 /* Gateway Timeout */ +#define OBX_RSP_HTTP_VER_NOT_SUPTD 0x55 /* HTTP version not supported */ +#define OBX_RSP_DATABASE_FULL 0x60 /* Database Full */ +#define OBX_RSP_DATABASE_LOCKED 0x61 /* Database Locked */ + +#define OBX_MAX_OK_RSP OBX_RSP_PART_CONTENT + +typedef UINT8 tOBX_RSP_CODE; + +/* tags for authentication triplet */ +#define OBX_NONCE_CHLNG_TAG 0 +#define OBX_OPTIONS_CHLNG_TAG 1 +#define OBX_REALM_CHLNG_TAG 2 + +#define OBX_DIGEST_RSP_TAG 0 +#define OBX_USERID_RSP_TAG 1 +#define OBX_NONCE_RSP_TAG 2 + +typedef struct +{ + UINT8 tag; + UINT8 len; + UINT8 *p_array; +} tOBX_TRIPLET; + +/* Server Callback type: */ +typedef void (tOBX_SR_CBACK) (tOBX_HANDLE shandle, tOBX_EVENT event, tOBX_EVT_PARAM param, BT_HDR *p_pkt); +/* Client Callback type: */ +typedef void (tOBX_CL_CBACK) (tOBX_HANDLE handle, tOBX_EVENT event, UINT8 rsp_code, tOBX_EVT_PARAM param, BT_HDR *p_pkt); + + +typedef struct +{ + UINT16 len; /* Length of target header. */ + UINT8 target[OBX_MAX_TARGET_LEN]; /* The byte sequence that describes the target header. */ +} tOBX_TARGET; + + +typedef struct +{ + tOBX_TARGET *p_target; + tOBX_SR_CBACK *p_cback; + UINT16 mtu; + UINT8 scn; /* The RFCOMM SCN number that this server listens for incoming requests. 0, if do not wish to listen to connection from RFCOMM. */ + BOOLEAN authenticate; + UINT8 auth_option; + UINT8 realm_charset; + UINT8 *p_realm; + UINT8 realm_len; + UINT8 max_sessions; + BOOLEAN get_nonf; /* report GET non-final request event. If FALSE, GET response is sent automatically */ + UINT16 psm; /* The L2CAP PSM number that this server listens for incoming requests. 0, if do not wish to listen to connection from L2CAP. */ + UINT32 nonce; /* This is converted to UINT8[16] internally before adding to the OBEX header. This value is copied to the server control block and is increased after each use. 0, if only legacy OBEX (unreliable) session is desired. */ + BOOLEAN srm; /* TRUE, to support single response mode. */ + UINT8 max_suspend; /* Max number of suspended session. must be less than OBX_MAX_SUSPEND_SESSIONS. ignored, if nonce is 0 */ +} tOBX_StartParams; + + + + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function OBX_Init +** +** Description This function is called to initialize the control block for this +** layer. It must be called before accessing any other of its API +** functions. It is typically called once during the start up of +** the stack. +** +** Returns void. +** +*******************************************************************************/ +OBX_API extern void OBX_Init(void); + +/******************************************************************************* +** +** Function OBX_SetTraceLevel +** +** Description This function sets the debug trace level for OBX. +** If 0xff is passed, the current trace level is returned. +** +** Input Parameters: +** level: The level to set the OBX tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +*******************************************************************************/ +OBX_API extern UINT8 OBX_SetTraceLevel (UINT8 level); + +/******************************************************************************* +** Function OBX_HandleToMtu +** +** Description Given an OBEX handle, return the associated peer MTU. +** +** Returns MTU. +** +*******************************************************************************/ +OBX_API extern UINT16 OBX_HandleToMtu(tOBX_HANDLE handle); + +/* Server API's: */ +/******************************************************************************* +** +** Function OBX_StartServer +** +** Description This function is to register a server entity to OBEX. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not have resources. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_StartServer(tOBX_StartParams *p_params, tOBX_HANDLE *p_handle); + +/******************************************************************************* +** +** Function OBX_StopServer +** +** Description This function is to stop this OBEX server from receiving any +** more incoming requests. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_StopServer(tOBX_HANDLE handle); + +/******************************************************************************* +** +** Function OBX_AddSuspendedSession +** +** Description This function is to add the session information for a previously +** suspended reliable session to the server control block +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_AddSuspendedSession(tOBX_HANDLE shandle, BD_ADDR peer_addr, UINT8 *p_sess_info, + UINT32 timeout, UINT8 ssn, UINT32 offset); + +/******************************************************************************* +** +** Function OBX_ConnectRsp +** +** Description This function is called to send the response to a Connect +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_ConnectRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_SessionRsp +** +** Description This function is called to respond to a request to create a reliable session. +** +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_SessionRsp(tOBX_HANDLE shandle, UINT8 rsp_code, + UINT8 ssn, UINT32 offset, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_SetPathRsp +** +** Description This function is called to send the response to a Set Path +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_SetPathRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_PutRsp +** +** Description This function is called to send the response to a Put +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_PutRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_GetRsp +** +** Description This function is called to send the response to a Get +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_GetRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_AbortRsp +** +** Description This function is called to send the response to an Abort +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_AbortRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_ActionRsp +** +** Description This function is called to respond to an Action command Request +** from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_ActionRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_DisconnectRsp +** +** Description This function is called to send the response to a Disconnect +** Request from an OBEX client. +** This function can also be used to force close the transport +** to a connected client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_DisconnectRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_Password +** +** Description This function is called to respond to an OBX_PASSWORD_EVT event. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_Password(tOBX_HANDLE shandle, UINT8 *p_password, UINT8 password_len, + UINT8 *p_userid, UINT8 userid_len); + +/******************************************************************************* +** +** Function OBX_GetPeerAddr +** +** Description This function is called to learn the Bluetooth address of the +** connected device. +** +** Returns L2CAP channel ID. +** +*******************************************************************************/ +OBX_API extern UINT16 OBX_GetPeerAddr(tOBX_HANDLE shandle, BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function OBX_GetPortHandle +** +** Description This function is called to get the RFCOMM port handle for the obex connection. +** +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if no existing connection. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_GetPortHandle(tOBX_HANDLE handle, UINT16 *port_handle); + +/* Client API's: */ +/******************************************************************************* +** +** Function OBX_ConnectReq +** +** Description This function registers a client entity to OBEX and sends a +** CONNECT request to the server specified by the API parameters. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_ConnectReq(BD_ADDR bd_addr, UINT8 scn, UINT16 mtu, + tOBX_CL_CBACK *p_cback, tOBX_HANDLE *p_handle, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_AllocSession +** +** Description This function registers a client entity to OBEX. +** If p_session_id is not NULL, it tries to find an suspended session +** with matching session_id. +** If scn is not 0, it allocates a control block for this new session. +** Otherwise, it allocates a control block for the given PSM. +** The associated virtual PSM assigned by L2CAP is returned in p_psm +** The allocated OBEX handle is returned in p_handle. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_AllocSession (UINT8 *p_session_info, UINT8 scn, UINT16 *p_psm, + tOBX_CL_CBACK *p_cback, tOBX_HANDLE *p_handle); + +/******************************************************************************* +** +** Function OBX_CreateSession +** +** Description This function registers a client entity to OBEX. +** It may send a CreateSession request and wait for CreateSession response. +** It sends a CONNECT request to the server specified by the API parameters. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_CreateSession (BD_ADDR bd_addr, UINT16 mtu, BOOLEAN srm, UINT32 nonce, + tOBX_HANDLE handle, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_ResumeSession +** +** Description This function registers a client entity to OBEX and resumes +** a previously interrupted reliable session. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_ResumeSession (BD_ADDR bd_addr, UINT8 ssn, UINT32 offset, tOBX_HANDLE handle); + +/******************************************************************************* +** +** Function OBX_SessionReq +** +** Description This function is used to Suspend, Resume, or Close a session. +** For Resume: this function registers a client entity to OBEX and resumes +** a previously interrupted reliable session. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_SessionReq (tOBX_HANDLE handle, tOBX_SESS_OP opcode, UINT32 timeout); + +/******************************************************************************* +** +** Function OBX_SetPathReq +** +** Description This function sends a Set Path request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_SetPathReq(tOBX_HANDLE handle, UINT8 flags, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_PutReq +** +** Description This function sends a Put request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_PutReq(tOBX_HANDLE handle, BOOLEAN final, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_GetReq +** +** Description This function sends a Get request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_GetReq(tOBX_HANDLE handle, BOOLEAN final, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_AbortReq +** +** Description This function sends an Abort request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_AbortReq(tOBX_HANDLE handle, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_DisconnectReq +** +** Description This function sends a Disconnect request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_DisconnectReq(tOBX_HANDLE handle, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_ActionReq +** +** Description This function sends a Action request to the connected server. +** The Name Header and DestName Header must be in p_pkt for +** the Copy and Move Object action. +** The Name header and Permission Header must be in p_pkt for +** the Set Object Permission action. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_ActionReq(tOBX_HANDLE handle, tOBX_ACTION action_id, BT_HDR *p_pkt); + +/******************************************************************************* +** +** Function OBX_AuthResponse +** +** Description This function is called to respond to an OBX_PASSWORD_EVT event. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +OBX_API extern tOBX_STATUS OBX_AuthResponse(tOBX_HANDLE handle, + UINT8 *p_password, UINT8 password_len, + UINT8 *p_userid, UINT8 userid_len, + BOOLEAN authenticate); + + +/******************************************************************************* +** +** Function OBX_HdrInit +** +** Description This function is called to initialize an OBEX packet. This +** function takes a GKI buffer and sets the offset in BT_HDR as +** OBEX_HDR_OFFSET, the len as 0. The layer_specific is set to the +** length still available. This function compares the given +** (pkt_size - sizeof(BT_HDR)) with the peer MTU to get the lesser +** of the two and set the layer_specific to +** (lesser_size - OBEX_HDR_OFFSET). +** If composing a header for the CONNECT request (there is no +** client handle yet), use OBX_HANDLE_NULL as the handle. +** +** Returns BT_HDR *. +** +*******************************************************************************/ +OBX_API extern BT_HDR * OBX_HdrInit(tOBX_HANDLE handle, UINT16 pkt_size); + +/******************************************************************************* +** +** Function OBX_AddNameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddNameHdr(BT_HDR *p_pkt, UINT16 *p_name, UINT16 len); + +/******************************************************************************* +** +** Function OBX_CharToWchar +** +** Description This function is called to convert ASCII to Unicode (UINT16). +** +** Returns the length. +** +*******************************************************************************/ +OBX_API extern UINT16 OBX_CharToWchar (UINT16 *w_str, char* a_str, UINT16 w_size); + +/******************************************************************************* +** +** Function OBX_AddAsciiNameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddAsciiNameHdr(BT_HDR *p_pkt, char *p_name); + +/******************************************************************************* +** +** Function OBX_AddTypeHdr +** +** Description This function is called to add an OBEX Type header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddTypeHdr(BT_HDR *p_pkt, char *p_type); + +/******************************************************************************* +** +** Function OBX_AddLengthHdr +** +** Description This function is called to add an OBEX Length header to an OBEX +** packet. The Length header describes the total length in bytes of +** the object. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddLengthHdr(BT_HDR *p_pkt, UINT32 len); + +/******************************************************************************* +** +** Function OBX_AddTimeHdr +** +** Description This function is called to add an OBEX Time header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddTimeHdr(BT_HDR *p_pkt, char *p_time); + +/******************************************************************************* +** +** Function OBX_AddDescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddDescrHdr(BT_HDR *p_pkt, UINT16 *p_descr, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddAsciiDescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddAsciiDescrHdr(BT_HDR *p_pkt, char *p_descr); + +/******************************************************************************* +** +** Function OBX_AddTargetHdr +** +** Description This function is called to add an OBEX Target header to an OBEX +** packet. This header is most commonly used in Connect packets. +** +** NOTE: The target header must be the first header in an OBEX message. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddTargetHdr(BT_HDR *p_pkt, UINT8 *p_target, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddHttpHdr +** +** Description This function is called to add an OBEX HTTP header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddHttpHdr(BT_HDR *p_pkt, UINT8 *p_http, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddBodyHdr +** +** Description This function is called to add an OBEX body header +** to an OBEX packet. +** +** NOTE: The body header must be the last header in an OBEX message. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddBodyHdr(BT_HDR *p_pkt, UINT8 *p_body, UINT16 len, BOOLEAN end); + +/* Alternate Body header functions: for non-blocking scenario */ +/******************************************************************************* +** +** Function OBX_AddBodyStart +** +** Description This function is called to get the address to the beginning of +** the byte sequence for an OBEX body header in an OBEX packet. +** +** Returns The address to add body content. +** +*******************************************************************************/ +OBX_API extern UINT8 *OBX_AddBodyStart(BT_HDR *p_pkt, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_AddBodyEnd +** +** Description This function is called to add the HI and the length of HV of an +** OBEX body header to an OBEX packet. If end is TRUE, HI is +** OBX_HI_BODY_END. If FALSE, HI is OBX_HI_BODY. It is assumed that +** the actual value of the body has been copied into the OBEX packet. +** +** Returns void +** +*******************************************************************************/ +OBX_API extern void OBX_AddBodyEnd(BT_HDR *p_pkt, UINT8 *p, UINT16 len, BOOLEAN end); + +/******************************************************************************* +** +** Function OBX_AddWhoHdr +** +** Description This function is called to add an OBEX Who header to an OBEX +** packet. +** +** Note: Who header is typically used in an OBEX CONNECT response packet +** to indicate the UUID of the service that has accepted the +** directed connection. If the server calls OBX_StartServer() with +** specified target header, this OBEX implementation automatically +** adds this WHO header to the CONNECT response packet. If +** OBX_StartServer() is called with target header length as 0, the +** OBEX API user is responsible to add the WHO header. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddWhoHdr(BT_HDR *p_pkt, UINT8 *p_who, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddAppParamHdr +** +** Description This function is called to add an OBEX Application Parameter +** header to an OBEX packet. This header is used by the application +** layer above OBEX to convey application specific information. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddAppParamHdr(BT_HDR *p_pkt, tOBX_TRIPLET *p_triplet, UINT8 num); + +/******************************************************************************* +** +** Function OBX_AddDestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddDestNameHdr(BT_HDR *p_pkt, UINT16 *p_dest, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddAsciiDestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddAsciiDestNameHdr(BT_HDR *p_pkt, char *p_descr); + +/******************************************************************************* +** +** Function OBX_AddPermissionHdr +** +** Description This function is called to add an OBEX Permission header to an OBEX +** packet. +** bit 0 is set for read permission +** bit 1 is set for write permission +** bit 2 is set for delete permission +** bit 7 is set for modify permission +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddPermissionHdr(BT_HDR *p_pkt, UINT8 user, UINT8 group, UINT8 other); + +/******************************************************************************* +** +** Function OBX_Add1ByteHdr +** +** Description This function is called to add a header with type as UINT8 +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_Add1ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT8 data); + +/******************************************************************************* +** +** Function OBX_Add4ByteHdr +** +** Description This function is called to add a header with type as UINT32 +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_Add4ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT32 data); + +/******************************************************************************* +** +** Function OBX_AddByteStrStart +** +** Description This function is called to get the address to the beginning of +** the byte sequence for an OBEX header in an OBEX packet. +** +** Returns The address to add the byte sequence. +** +*******************************************************************************/ +OBX_API extern UINT8 *OBX_AddByteStrStart(BT_HDR *p_pkt, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_AddByteStrHdr +** +** Description This function is called to add a header with type as byte sequence +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddByteStrHdr(BT_HDR *p_pkt, UINT8 id, UINT8 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddUnicodeHdr +** +** Description This function is called to add a header with type as Unicode string +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddUnicodeHdr(BT_HDR *p_pkt, UINT8 id, UINT16 *p_data, UINT16 len); + +/******************************************************************************* +** +** Function OBX_AddTriplet +** +** Description This function is called to add a header with type as byte sequence +** to an OBEX packet. +** +** Note: The byte sequence uses a Tag-Length-Value encoding scheme +** These headers include: Application Parameters header +** Authenticate Challenge header +** Authenticate Response header +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddTriplet(BT_HDR *p_pkt, UINT8 id, tOBX_TRIPLET *p_triplet, UINT8 num); + +/******************************************************************************* +** +** Function OBX_CheckHdr +** +** Description This function is called to check if the given OBEX packet +** contains the specified header. +** +** Returns NULL, if the header is not in the OBEX packet. +** The pointer to the specified header beginning from HI. +** +*******************************************************************************/ +OBX_API extern UINT8 * OBX_CheckHdr(BT_HDR *p_pkt, UINT8 id); + +/******************************************************************************* +** +** Function OBX_ReadNumHdrs +** +** Description This function is called to check the number of headers in the +** given OBEX packet +** +** Returns number of headers. +** +*******************************************************************************/ +OBX_API extern UINT8 OBX_ReadNumHdrs(BT_HDR *p_pkt, UINT8 *p_num_body); + +/******************************************************************************* +** +** Function OBX_ReadHdrLen +** +** Description This function is called to check the length of the specified +** header in the given OBEX packet. +** +** Returns OBX_INVALID_HDR_LEN, if the header is not in the OBEX packet. +** Otherwise the actual length of the header. +** +*******************************************************************************/ +OBX_API extern UINT16 OBX_ReadHdrLen(BT_HDR *p_pkt, UINT8 id); + +/******************************************************************************* +** +** Function OBX_ReadNameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadNameHdr(BT_HDR *p_pkt, UINT16 *p_name, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_WcharToChar +** +** Description This function is called to convert Unicode (UINT16) to ASCII. +** +** Returns void. +** +*******************************************************************************/ +OBX_API extern void OBX_WcharToChar (char *a_str, UINT16* w_str, UINT16 a_size) ; + +/******************************************************************************* +** +** Function OBX_ReadAsciiNameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. If Name header exists in the given OBEX packet, +** it is converted to ASCII format and copied into p_name. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadAsciiNameHdr(BT_HDR *p_pkt, char *p_name, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_ReadTypeHdr +** +** Description This function is called to get the Type Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadTypeHdr(BT_HDR *p_pkt, UINT8 **p_type, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadLengthHdr +** +** Description This function is called to get the Length Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadLengthHdr(BT_HDR *p_pkt, UINT32 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadTimeHdr +** +** Description This function is called to get the Time Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadTimeHdr(BT_HDR *p_pkt, UINT8 **p_time, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadDescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadDescrHdr(BT_HDR *p_pkt, UINT16 *p_descr, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadDestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadDestNameHdr(BT_HDR *p_pkt, UINT16 *p_dest, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadAsciiDescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. If Description header exists in the given +** OBEX packet, it is converted to ASCII format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadAsciiDescrHdr(BT_HDR *p_pkt, char *p_descr, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_ReadAsciiDestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. If DestName header exists in the given +** OBEX packet, it is converted to ASCII format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadAsciiDestNameHdr(BT_HDR *p_pkt, char *p_dest, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_ReadTargetHdr +** +** Description This function is called to get the Target Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadTargetHdr(BT_HDR *p_pkt, UINT8 **p_target, UINT16 *p_len, UINT8 next); + +/******************************************************************************* +** +** Function OBX_ReadHttpHdr +** +** Description This function is called to get the HTTP Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadHttpHdr(BT_HDR *p_pkt, UINT8 **p_http, UINT16 *p_len, UINT8 next); + +/******************************************************************************* +** +** Function OBX_ReadBodyHdr +** +** Description This function is called to get the Body Header in the +** given OBEX packet. +** +** Returns 1, if a single header is in the OBEX packet. +** 2, if a end of body header is in the OBEX packet. +** 0, (FALSE) otherwise. +** +*******************************************************************************/ +OBX_API extern UINT8 OBX_ReadBodyHdr(BT_HDR *p_pkt, UINT8 **p_body, UINT16 *p_len, BOOLEAN *p_end); + +/******************************************************************************* +** +** Function OBX_ReadWhoHdr +** +** Description This function is called to get the Who Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadWhoHdr(BT_HDR *p_pkt, UINT8 **p_who, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadAppParamHdr +** +** Description This function is called to get the Application Parameter Header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadAppParamHdr(BT_HDR *p_pkt, UINT8 *p_tag, UINT8 **p_app_param, UINT8 *p_len, UINT8 next); + +/******************************************************************************* +** +** Function OBX_ReadPermissionHdr +** +** Description This function is called to get the Application Parameter Header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadPermissionHdr(BT_HDR *p_pkt, UINT8 *p_user, UINT8 *p_group, UINT8 *p_other); + +/******************************************************************************* +** +** Function OBX_Read1ByteHdr +** +** Description This function is called to get the UINT8 HV of the given HI +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_Read1ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT8 *p_data); + +/******************************************************************************* +** +** Function OBX_Read4ByteHdr +** +** Description This function is called to get the UINT32 HV of the given HI +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_Read4ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT32 *p_data); + +/******************************************************************************* +** +** Function OBX_ReadByteStrHdr +** +** Description This function is called to get the byte sequence HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadByteStrHdr(BT_HDR *p_pkt, UINT8 id, UINT8 **p_data, UINT16 *p_len, UINT8 next); + +/******************************************************************************* +** +** Function OBX_ReadUnicodeHdr +** +** Description This function is called to get the Unicode HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadUnicodeHdr(BT_HDR *p_pkt, UINT8 id, UINT16 *p_data, UINT16 *p_len); + +/******************************************************************************* +** +** Function OBX_ReadTriplet +** +** Description This function is called to get the Triplet HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadTriplet(BT_HDR *p_pkt, UINT8 id, tOBX_TRIPLET *p_triplet, UINT8 *p_num); + +/******************************************************************************* +** +** Function OBX_ReadActionIdHdr +** +** Description This function is called to get the HV of the Action ID header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadActionIdHdr(BT_HDR *p_pkt, UINT8 *p_data); + +/******************************************************************************* +** +** Function OBX_ReadChallenge +** +** Description This function is called to read the Realm and options of the +** Authentication Challenge Header in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadChallenge(BT_HDR *p_pkt, tOBX_CHARSET *p_charset, + UINT8 **p_realm, UINT8 *p_len, + tOBX_AUTH_OPT *p_option); + +/******************************************************************************* +** +** Function OBX_ReadAuthParams +** +** Description This function is called to read the User ID of the +** Authentication Response Header in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadAuthParams(BT_HDR *p_pkt, UINT8 **p_userid, UINT8 *p_len, + BOOLEAN *is_challenged, tOBX_AUTH_OPT *p_option); + + +/******************************************************************************* +** +** Function utfc_16_to_8 +** +** Description Convert a UTF-16 array to a null-terminated UTF-8 string. +** Illegal characters are skipped. +** +** Returns Length of UTF-8 string in bytes. +** +*******************************************************************************/ +OBX_API extern UINT16 utfc_16_to_8(UINT8 *p_utf8, UINT16 utf8_len, UINT16 *p_utf16, UINT16 utf16_len); + +/******************************************************************************* +** +** Function utfc_8_to_16 +** +** Description Convert a null-terminated UTF-8 string to a UTF-16 array. +** Illegal characters are skipped. +** +** Returns Length of UTF-16 array. +** +*******************************************************************************/ +OBX_API extern UINT16 utfc_8_to_16(UINT16 *p_utf16, UINT16 utf16_len, UINT8 *p_utf8); + +/******************************************************************************* +** +** Function OBX_AddUtf8NameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddUtf8NameHdr(BT_HDR *p_pkt, UINT8 *p_name); + +/******************************************************************************* +** +** Function OBX_AddUtf8DescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddUtf8DescrHdr(BT_HDR *p_pkt, UINT8 *p_descr); + +/******************************************************************************* +** +** Function OBX_AddUtf8DestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_AddUtf8DestNameHdr(BT_HDR *p_pkt, UINT8 *p_dest); + +/******************************************************************************* +** +** Function OBX_ReadUtf8NameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. If Name header exists in the given OBEX packet, +** it is converted to UTF8 format and copied into p_name. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadUtf8NameHdr(BT_HDR *p_pkt, UINT8 *p_name, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_ReadUtf8DescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. If Description header exists in the given +** OBEX packet, it is converted to UTF8 format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadUtf8DescrHdr(BT_HDR *p_pkt, UINT8 *p_descr, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_ReadUtf8DestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_ReadUtf8DestNameHdr(BT_HDR *p_pkt, UINT8 *p_dest, UINT16 max_len); + +/******************************************************************************* +** +** Function OBX_VerifyResponse +** +** Description This function is called by the client to verify the challenge +** response. +** +** Returns TRUE, if successful. +** FALSE, if authentication failed +** +*******************************************************************************/ +OBX_API extern BOOLEAN OBX_VerifyResponse(UINT32 nonce_u32, UINT8 *p_password, UINT8 password_len, UINT8 *p_response); + +#ifdef __cplusplus +} +#endif + +#endif /* OBX_API_H */ diff --git a/stack/include/pan_api.h b/stack/include/pan_api.h new file mode 100644 index 0000000..a604463 --- /dev/null +++ b/stack/include/pan_api.h @@ -0,0 +1,459 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the PAN API definitions + * + ******************************************************************************/ +#ifndef PAN_API_H +#define PAN_API_H + +#include "bnep_api.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Define the minimum offset needed in a GKI buffer for +** sending PAN packets. Note, we are currently not sending +** extension headers, but may in the future, so allow +** space for them +*/ +#define PAN_MINIMUM_OFFSET BNEP_MINIMUM_OFFSET + + +/* +** The handle is passed from BNEP to PAN. The same handle is used +** between PAN and application as well +*/ +#define PAN_INVALID_HANDLE BNEP_INVALID_HANDLE + +/* Bit map for PAN roles */ +#define PAN_ROLE_CLIENT 0x01 /* PANU role */ +#define PAN_ROLE_GN_SERVER 0x02 /* GN role */ +#define PAN_ROLE_NAP_SERVER 0x04 /* NAP role */ + +/* Bitmap to indicate the usage of the Data */ +#define PAN_DATA_TO_HOST 0x01 +#define PAN_DATA_TO_LAN 0x02 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Define the result codes from PAN */ +enum +{ + PAN_SUCCESS, /* Success */ + PAN_DISCONNECTED = BNEP_CONN_DISCONNECTED, /* Connection terminated */ + PAN_CONN_FAILED = BNEP_CONN_FAILED, /* Connection failed */ + PAN_NO_RESOURCES = BNEP_NO_RESOURCES, /* No resources */ + PAN_MTU_EXCEDED = BNEP_MTU_EXCEDED, /* Attempt to write long data */ + PAN_INVALID_OFFSET = BNEP_INVALID_OFFSET, /* Insufficient offset in GKI buffer */ + PAN_CONN_FAILED_CFG = BNEP_CONN_FAILED_CFG, /* Connection failed cos of config */ + PAN_INVALID_SRC_ROLE = BNEP_CONN_FAILED_SRC_UUID, /* Connection failed wrong source UUID */ + PAN_INVALID_DST_ROLE = BNEP_CONN_FAILED_DST_UUID, /* Connection failed wrong destination UUID */ + PAN_CONN_FAILED_UUID_SIZE = BNEP_CONN_FAILED_UUID_SIZE, /* Connection failed wrong size UUID */ + PAN_Q_SIZE_EXCEEDED = BNEP_Q_SIZE_EXCEEDED, /* Too many buffers to dest */ + PAN_TOO_MANY_FILTERS = BNEP_TOO_MANY_FILTERS, /* Too many local filters specified */ + PAN_SET_FILTER_FAIL = BNEP_SET_FILTER_FAIL, /* Set Filter failed */ + PAN_WRONG_HANDLE = BNEP_WRONG_HANDLE, /* Wrong handle for the connection */ + PAN_WRONG_STATE = BNEP_WRONG_STATE, /* Connection is in wrong state */ + PAN_SECURITY_FAIL = BNEP_SECURITY_FAIL, /* Failed because of security */ + PAN_IGNORE_CMD = BNEP_IGNORE_CMD, /* To ignore the rcvd command */ + PAN_TX_FLOW_ON = BNEP_TX_FLOW_ON, /* tx data flow enabled */ + PAN_TX_FLOW_OFF = BNEP_TX_FLOW_OFF, /* tx data flow disabled */ + PAN_FAILURE /* Failure */ + +}; +typedef UINT8 tPAN_RESULT; + + +/***************************************************************** +** Callback Function Prototypes +*****************************************************************/ + +/* This is call back function used to report connection status +** to the application. The second parameter TRUE means +** to create the bridge and FALSE means to remove it. +*/ +typedef void (tPAN_CONN_STATE_CB) (UINT16 handle, BD_ADDR bd_addr, tPAN_RESULT state, BOOLEAN is_role_change, + UINT8 src_role, UINT8 dst_role); + + +/* This is call back function used to create bridge for the +** Connected device. The parameter "state" indicates +** whether to create the bridge or remove it. TRUE means +** to create the bridge and FALSE means to remove it. +*/ +typedef void (tPAN_BRIDGE_REQ_CB) (BD_ADDR bd_addr, BOOLEAN state); + + +/* Data received indication callback prototype. Parameters are +** Source BD/Ethernet Address +** Dest BD/Ethernet address +** Protocol +** Address of buffer (or data if non-GKI) +** Length of data (non-GKI) +** ext is flag to indicate whether it has aby extension headers +** Flag used to indicate to forward on LAN +** FALSE - Use it for internal stack +** TRUE - Send it across the ethernet as well +*/ +typedef void (tPAN_DATA_IND_CB) (UINT16 handle, + BD_ADDR src, + BD_ADDR dst, + UINT16 protocol, + UINT8 *p_data, + UINT16 len, + BOOLEAN ext, + BOOLEAN forward); + + +/* Data buffer received indication callback prototype. Parameters are +** Source BD/Ethernet Address +** Dest BD/Ethernet address +** Protocol +** pointer to the data buffer +** ext is flag to indicate whether it has aby extension headers +** Flag used to indicate to forward on LAN +** FALSE - Use it for internal stack +** TRUE - Send it across the ethernet as well +*/ +typedef void (tPAN_DATA_BUF_IND_CB) (UINT16 handle, + BD_ADDR src, + BD_ADDR dst, + UINT16 protocol, + BT_HDR *p_buf, + BOOLEAN ext, + BOOLEAN forward); + + +/* Flow control callback for TX data. Parameters are +** Handle to the connection +** Event flow status +*/ +typedef void (tPAN_TX_DATA_FLOW_CB) (UINT16 handle, + tPAN_RESULT event); + +/* Filters received indication callback prototype. Parameters are +** Handle to the connection +** TRUE if the cb is called for indication +** Ignore this if it is indication, otherwise it is the result +** for the filter set operation performed by the local +** device +** Number of protocol filters present +** Pointer to the filters start. Filters are present in pairs +** of start of the range and end of the range. +** They will be present in big endian order. First +** two bytes will be starting of the first range and +** next two bytes will be ending of the range. +*/ +typedef void (tPAN_FILTER_IND_CB) (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters); + + + +/* Multicast Filters received indication callback prototype. Parameters are +** Handle to the connection +** TRUE if the cb is called for indication +** Ignore this if it is indication, otherwise it is the result +** for the filter set operation performed by the local +** device +** Number of multicast filters present +** Pointer to the filters start. Filters are present in pairs +** of start of the range and end of the range. +** First six bytes will be starting of the first range and +** next six bytes will be ending of the range. +*/ +typedef void (tPAN_MFILTER_IND_CB) (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_mfilters, + UINT8 *p_mfilters); + + + + +/* This structure is used to register with PAN profile +** It is passed as a parameter to PAN_Register call. +*/ +typedef struct +{ + tPAN_CONN_STATE_CB *pan_conn_state_cb; /* Connection state callback */ + tPAN_BRIDGE_REQ_CB *pan_bridge_req_cb; /* Bridge request callback */ + tPAN_DATA_IND_CB *pan_data_ind_cb; /* Data indication callback */ + tPAN_DATA_BUF_IND_CB *pan_data_buf_ind_cb; /* Data buffer indication callback */ + tPAN_FILTER_IND_CB *pan_pfilt_ind_cb; /* protocol filter indication callback */ + tPAN_MFILTER_IND_CB *pan_mfilt_ind_cb; /* multicast filter indication callback */ + tPAN_TX_DATA_FLOW_CB *pan_tx_data_flow_cb; /* data flow callback */ + char *user_service_name; /* Service name for PANU role */ + char *gn_service_name; /* Service name for GN role */ + char *nap_service_name; /* Service name for NAP role */ + +} tPAN_REGISTER; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function PAN_Register +** +** Description This function is called by the application to register +** its callbacks with PAN profile. The application then +** should set the PAN role explicitly. +** +** Parameters: p_register - contains all callback function pointers +** +** +** Returns none +** +*******************************************************************************/ +PAN_API extern void PAN_Register (tPAN_REGISTER *p_register); + + +/******************************************************************************* +** +** Function PAN_Deregister +** +** Description This function is called by the application to de-register +** its callbacks with PAN profile. This will make the PAN to +** become inactive. This will deregister PAN services from SDP +** and close all active connections +** +** Returns none +** +*******************************************************************************/ +PAN_API extern void PAN_Deregister (void); + +/******************************************************************************* +** +** Function PAN_SetRole +** +** Description This function is called by the application to set the PAN +** profile role. This should be called after PAN_Register. +** This can be called any time to change the PAN role +** +** Parameters: role - is bit map of roles to be active +** PAN_ROLE_CLIENT is for PANU role +** PAN_ROLE_GN_SERVER is for GN role +** PAN_ROLE_NAP_SERVER is for NAP role +** sec_mask - Security mask for different roles +** It is array of UINT8. The byte represent the +** security for roles PANU, GN and NAP in order +** p_user_name - Service name for PANU role +** p_gn_name - Service name for GN role +** p_nap_name - Service name for NAP role +** Can be NULL if user wants it to be default +** +** Returns PAN_SUCCESS - if the role is set successfully +** PAN_FAILURE - if the role is not valid +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_SetRole (UINT8 role, + UINT8 *sec_mask, + char *p_user_name, + char *p_gn_name, + char *p_nap_name); + +/******************************************************************************* +** +** Function PAN_Connect +** +** Description This function is called by the application to initiate a +** connection to the remote device +** +** Parameters: rem_bda - BD Addr of the remote device +** src_role - Role of the local device for the connection +** dst_role - Role of the remote device for the connection +** PAN_ROLE_CLIENT is for PANU role +** PAN_ROLE_GN_SERVER is for GN role +** PAN_ROLE_NAP_SERVER is for NAP role +** *handle - Pointer for returning Handle to the connection +** +** Returns PAN_SUCCESS - if the connection is initiated successfully +** PAN_NO_RESOURCES - resources are not sufficent +** PAN_FAILURE - if the connection cannot be initiated +** this can be because of the combination of +** src and dst roles may not be valid or +** allowed at that point of time +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_Connect (BD_ADDR rem_bda, UINT8 src_role, UINT8 dst_role, UINT16 *handle); + +/******************************************************************************* +** +** Function PAN_Disconnect +** +** Description This is used to disconnect the connection +** +** Parameters: handle - handle for the connection +** +** Returns PAN_SUCCESS - if the connection is closed successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in disconnecting +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_Disconnect (UINT16 handle); + +/******************************************************************************* +** +** Function PAN_Write +** +** Description This sends data over the PAN connections. If this is called +** on GN or NAP side and the packet is multicast or broadcast +** it will be sent on all the links. Otherwise the correct link +** is found based on the destination address and forwarded on it +** If the return value is not PAN_SUCCESS the application should +** take care of releasing the message buffer +** +** Parameters: dst - MAC or BD Addr of the destination device +** src - MAC or BD Addr of the source who sent this packet +** protocol - protocol of the ethernet packet like IP or ARP +** p_data - pointer to the data +** len - length of the data +** ext - to indicate that extension headers present +** +** Returns PAN_SUCCESS - if the data is sent successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in sending data +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_Write (UINT16 handle, + BD_ADDR dst, + BD_ADDR src, + UINT16 protocol, + UINT8 *p_data, + UINT16 len, + BOOLEAN ext); + +/******************************************************************************* +** +** Function PAN_WriteBuf +** +** Description This sends data over the PAN connections. If this is called +** on GN or NAP side and the packet is multicast or broadcast +** it will be sent on all the links. Otherwise the correct link +** is found based on the destination address and forwarded on it +** If the return value is not PAN_SUCCESS the application should +** take care of releasing the message buffer +** +** Parameters: dst - MAC or BD Addr of the destination device +** src - MAC or BD Addr of the source who sent this packet +** protocol - protocol of the ethernet packet like IP or ARP +** p_buf - pointer to the data buffer +** ext - to indicate that extension headers present +** +** Returns PAN_SUCCESS - if the data is sent successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in sending data +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_WriteBuf (UINT16 handle, + BD_ADDR dst, + BD_ADDR src, + UINT16 protocol, + BT_HDR *p_buf, + BOOLEAN ext); + +/******************************************************************************* +** +** Function PAN_SetProtocolFilters +** +** Description This function is used to set protocol filters on the peer +** +** Parameters: handle - handle for the connection +** num_filters - number of protocol filter ranges +** start - array of starting protocol numbers +** end - array of ending protocol numbers +** +** +** Returns PAN_SUCCESS if protocol filters are set successfully +** PAN_FAILURE if connection not found or error in setting +** +*******************************************************************************/ +PAN_API extern tPAN_RESULT PAN_SetProtocolFilters (UINT16 handle, + UINT16 num_filters, + UINT16 *p_start_array, + UINT16 *p_end_array); + +/******************************************************************************* +** +** Function PAN_SetMulticastFilters +** +** Description This function is used to set multicast filters on the peer +** +** Parameters: handle - handle for the connection +** num_filters - number of multicast filter ranges +** p_start_array - Pointer to sequence of beginings of all +** multicast address ranges +** p_end_array - Pointer to sequence of ends of all +** multicast address ranges +** +** +** Returns PAN_SUCCESS if multicast filters are set successfully +** PAN_FAILURE if connection not found or error in setting +** +*******************************************************************************/ +PAN_API extern tBNEP_RESULT PAN_SetMulticastFilters (UINT16 handle, + UINT16 num_mcast_filters, + UINT8 *p_start_array, + UINT8 *p_end_array); + +/******************************************************************************* +** +** Function PAN_SetTraceLevel +** +** Description This function sets the trace level for PAN. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +PAN_API extern UINT8 PAN_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function PAN_Init +** +** Description This function initializes the PAN unit. It should be called +** before accessing any other APIs to initialize the control +** block. +** +** Returns void +** +*******************************************************************************/ +PAN_API extern void PAN_Init (void); + +#ifdef __cplusplus +} +#endif + +#endif /* PAN_API_H */ diff --git a/stack/include/port_api.h b/stack/include/port_api.h new file mode 100644 index 0000000..2f64932 --- /dev/null +++ b/stack/include/port_api.h @@ -0,0 +1,635 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the PORT API definitions + * + ******************************************************************************/ +#ifndef PORT_API_H +#define PORT_API_H + +#include "bt_target.h" + +/***************************************************************************** +** Constants and Types +*****************************************************************************/ + +/* +** Define port settings structure send from the application in the +** set settings request, or to the application in the set settings indication. +*/ +typedef struct +{ + +#define PORT_BAUD_RATE_2400 0x00 +#define PORT_BAUD_RATE_4800 0x01 +#define PORT_BAUD_RATE_7200 0x02 +#define PORT_BAUD_RATE_9600 0x03 +#define PORT_BAUD_RATE_19200 0x04 +#define PORT_BAUD_RATE_38400 0x05 +#define PORT_BAUD_RATE_57600 0x06 +#define PORT_BAUD_RATE_115200 0x07 +#define PORT_BAUD_RATE_230400 0x08 + + UINT8 baud_rate; + +#define PORT_5_BITS 0x00 +#define PORT_6_BITS 0x01 +#define PORT_7_BITS 0x02 +#define PORT_8_BITS 0x03 + + UINT8 byte_size; + +#define PORT_ONESTOPBIT 0x00 +#define PORT_ONE5STOPBITS 0x01 + UINT8 stop_bits; + +#define PORT_PARITY_NO 0x00 +#define PORT_PARITY_YES 0x01 + UINT8 parity; + +#define PORT_ODD_PARITY 0x00 +#define PORT_EVEN_PARITY 0x01 +#define PORT_MARK_PARITY 0x02 +#define PORT_SPACE_PARITY 0x03 + + UINT8 parity_type; + +#define PORT_FC_OFF 0x00 +#define PORT_FC_XONXOFF_ON_INPUT 0x01 +#define PORT_FC_XONXOFF_ON_OUTPUT 0x02 +#define PORT_FC_CTS_ON_INPUT 0x04 +#define PORT_FC_CTS_ON_OUTPUT 0x08 +#define PORT_FC_DSR_ON_INPUT 0x10 +#define PORT_FC_DSR_ON_OUTPUT 0x20 + + UINT8 fc_type; + + UINT8 rx_char1; + +#define PORT_XON_DC1 0x11 + UINT8 xon_char; + +#define PORT_XOFF_DC3 0x13 + UINT8 xoff_char; + +} tPORT_STATE; + + +/* +** Define the callback function prototypes. Parameters are specific +** to each event and are described bellow +*/ +typedef int (tPORT_DATA_CALLBACK) (UINT16 port_handle, void *p_data, UINT16 len); + +#define DATA_CO_CALLBACK_TYPE_INCOMING 1 +#define DATA_CO_CALLBACK_TYPE_OUTGOING_SIZE 2 +#define DATA_CO_CALLBACK_TYPE_OUTGOING 3 +typedef int (tPORT_DATA_CO_CALLBACK) (UINT16 port_handle, UINT8* p_buf, UINT16 len, int type); + +typedef void (tPORT_CALLBACK) (UINT32 code, UINT16 port_handle); + +/* +** Define events that registered application can receive in the callback +*/ + +#define PORT_EV_RXCHAR 0x00000001 /* Any Character received */ +#define PORT_EV_RXFLAG 0x00000002 /* Received certain character */ +#define PORT_EV_TXEMPTY 0x00000004 /* Transmitt Queue Empty */ +#define PORT_EV_CTS 0x00000008 /* CTS changed state */ +#define PORT_EV_DSR 0x00000010 /* DSR changed state */ +#define PORT_EV_RLSD 0x00000020 /* RLSD changed state */ +#define PORT_EV_BREAK 0x00000040 /* BREAK received */ +#define PORT_EV_ERR 0x00000080 /* Line status error occurred */ +#define PORT_EV_RING 0x00000100 /* Ring signal detected */ +#define PORT_EV_CTSS 0x00000400 /* CTS state */ +#define PORT_EV_DSRS 0x00000800 /* DSR state */ +#define PORT_EV_RLSDS 0x00001000 /* RLSD state */ +#define PORT_EV_OVERRUN 0x00002000 /* receiver buffer overrun */ +#define PORT_EV_TXCHAR 0x00004000 /* Any character transmitted */ + +#define PORT_EV_CONNECTED 0x00000200 /* RFCOMM connection established */ +#define PORT_EV_CONNECT_ERR 0x00008000 /* Was not able to establish connection */ + /* or disconnected */ +#define PORT_EV_FC 0x00010000 /* data flow enabled flag changed by remote */ +#define PORT_EV_FCS 0x00020000 /* data flow enable status true = enabled */ + +/* +** To register for events application should provide bitmask with +** corresponding bit set +*/ + +#define PORT_MASK_ALL (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | PORT_EV_CTS | \ + PORT_EV_DSR | PORT_EV_RLSD | PORT_EV_BREAK | \ + PORT_EV_ERR | PORT_EV_RING | PORT_EV_CONNECT_ERR | \ + PORT_EV_DSRS | PORT_EV_CTSS | PORT_EV_RLSDS | \ + PORT_EV_RXFLAG | PORT_EV_TXCHAR | PORT_EV_OVERRUN | \ + PORT_EV_FC | PORT_EV_FCS | PORT_EV_CONNECTED) + + +/* +** Define port result codes +*/ +#define PORT_SUCCESS 0 + +#define PORT_ERR_BASE 0 + +#define PORT_UNKNOWN_ERROR (PORT_ERR_BASE + 1) +#define PORT_ALREADY_OPENED (PORT_ERR_BASE + 2) +#define PORT_CMD_PENDING (PORT_ERR_BASE + 3) +#define PORT_APP_NOT_REGISTERED (PORT_ERR_BASE + 4) +#define PORT_NO_MEM (PORT_ERR_BASE + 5) +#define PORT_NO_RESOURCES (PORT_ERR_BASE + 6) +#define PORT_BAD_BD_ADDR (PORT_ERR_BASE + 7) +#define PORT_BAD_HANDLE (PORT_ERR_BASE + 9) +#define PORT_NOT_OPENED (PORT_ERR_BASE + 10) +#define PORT_LINE_ERR (PORT_ERR_BASE + 11) +#define PORT_START_FAILED (PORT_ERR_BASE + 12) +#define PORT_PAR_NEG_FAILED (PORT_ERR_BASE + 13) +#define PORT_PORT_NEG_FAILED (PORT_ERR_BASE + 14) +#define PORT_SEC_FAILED (PORT_ERR_BASE + 15) +#define PORT_PEER_CONNECTION_FAILED (PORT_ERR_BASE + 16) +#define PORT_PEER_FAILED (PORT_ERR_BASE + 17) +#define PORT_PEER_TIMEOUT (PORT_ERR_BASE + 18) +#define PORT_CLOSED (PORT_ERR_BASE + 19) +#define PORT_TX_FULL (PORT_ERR_BASE + 20) +#define PORT_LOCAL_CLOSED (PORT_ERR_BASE + 21) +#define PORT_LOCAL_TIMEOUT (PORT_ERR_BASE + 22) +#define PORT_TX_QUEUE_DISABLED (PORT_ERR_BASE + 23) +#define PORT_PAGE_TIMEOUT (PORT_ERR_BASE + 24) +#define PORT_INVALID_SCN (PORT_ERR_BASE + 25) + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function RFCOMM_CreateConnection +** +** Description RFCOMM_CreateConnection function is used from the application +** to establish serial port connection to the peer device, +** or allow RFCOMM to accept a connection from the peer +** application. +** +** Parameters: scn - Service Channel Number as registered with +** the SDP (server) or obtained using SDP from +** the peer device (client). +** is_server - TRUE if requesting application is a server +** mtu - Maximum frame size the application can accept +** bd_addr - BD_ADDR of the peer (client) +** mask - specifies events to be enabled. A value +** of zero disables all events. +** p_handle - OUT pointer to the handle. +** p_mgmt_cb - pointer to callback function to receive +** connection up/down events. +** Notes: +** +** Server can call this function with the same scn parameter multiple times if +** it is ready to accept multiple simulteneous connections. +** +** DLCI for the connection is (scn * 2 + 1) if client originates connection on +** existing none initiator multiplexer channel. Otherwise it is (scn * 2). +** For the server DLCI can be changed later if client will be calling it using +** (scn * 2 + 1) dlci. +** +*******************************************************************************/ +RFC_API extern int RFCOMM_CreateConnection (UINT16 uuid, UINT8 scn, + BOOLEAN is_server, UINT16 mtu, + BD_ADDR bd_addr, UINT16 *p_handle, + tPORT_CALLBACK *p_mgmt_cb); + + +/******************************************************************************* +** +** Function RFCOMM_RemoveConnection +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** +*******************************************************************************/ +RFC_API extern int RFCOMM_RemoveConnection (UINT16 handle); + + +/******************************************************************************* +** +** Function RFCOMM_RemoveServer +** +** Description This function is called to close the server port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +RFC_API extern int RFCOMM_RemoveServer (UINT16 handle); + + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description Set event callback the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** p_callback - address of the callback function which should +** be called from the RFCOMM when an event +** specified in the mask occurs. +** +*******************************************************************************/ +RFC_API extern int PORT_SetEventCallback (UINT16 port_handle, + tPORT_CALLBACK *p_port_cb); + + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description Set event data callback the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** p_callback - address of the callback function which should +** be called from the RFCOMM when a data +** packet is received. +** +*******************************************************************************/ +RFC_API extern int PORT_SetDataCallback (UINT16 port_handle, + tPORT_DATA_CALLBACK *p_cb); + +RFC_API extern int PORT_SetDataCOCallback (UINT16 port_handle, tPORT_DATA_CO_CALLBACK *p_port_cb); +/******************************************************************************* +** +** Function PORT_SetEventMask +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle of the port returned in the Open +** mask - specifies events to be enabled. A value +** of zero disables all events. +** +*******************************************************************************/ +RFC_API extern int PORT_SetEventMask (UINT16 port_handle, UINT32 mask); + + +/******************************************************************************* +** +** Function PORT_CheckConnection +** +** Description This function returns PORT_SUCCESS if connection referenced +** by handle is up and running +** +** Parameters: handle - Handle of the port returned in the Open +** bd_addr - OUT bd_addr of the peer +** p_lcid - OUT L2CAP's LCID +** +*******************************************************************************/ +RFC_API extern int PORT_CheckConnection (UINT16 handle, BD_ADDR bd_addr, + UINT16 *p_lcid); + +/******************************************************************************* +** +** Function PORT_IsOpening +** +** Description This function returns TRUE if there is any RFCOMM connection +** opening in process. +** +** Parameters: TRUE if any connection opening is found +** bd_addr - bd_addr of the peer +** +*******************************************************************************/ +RFC_API extern BOOLEAN PORT_IsOpening (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function PORT_SetState +** +** Description This function configures connection according to the +** specifications in the tPORT_STATE structure. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure containing +** configuration information for the connection. +** +*******************************************************************************/ +RFC_API extern int PORT_SetState (UINT16 handle, tPORT_STATE *p_settings); + +/******************************************************************************* +** +** Function PORT_GetRxQueueCnt +** +** Description This function return number of buffers on the rx queue. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_rx_queue_count - Pointer to return queue count in. +** +*******************************************************************************/ +RFC_API extern int PORT_GetRxQueueCnt (UINT16 handle, UINT16 *p_rx_queue_count); + +/******************************************************************************* +** +** Function PORT_GetState +** +** Description This function is called to fill tPORT_STATE structure +** with the current control settings for the port +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure in which +** configuration information is returned. +** +*******************************************************************************/ +RFC_API extern int PORT_GetState (UINT16 handle, tPORT_STATE *p_settings); + + +/******************************************************************************* +** +** Function PORT_Control +** +** Description This function directs a specified connection to pass control +** control information to the peer device. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** signal - specify the function to be passed +** +*******************************************************************************/ +#define PORT_SET_DTRDSR 0x01 +#define PORT_CLR_DTRDSR 0x02 +#define PORT_SET_CTSRTS 0x03 +#define PORT_CLR_CTSRTS 0x04 +#define PORT_SET_RI 0x05 /* DCE only */ +#define PORT_CLR_RI 0x06 /* DCE only */ +#define PORT_SET_DCD 0x07 /* DCE only */ +#define PORT_CLR_DCD 0x08 /* DCE only */ +#define PORT_BREAK 0x09 /* Break event */ + +RFC_API extern int PORT_Control (UINT16 handle, UINT8 signal); + + +/******************************************************************************* +** +** Function PORT_FlowControl +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +RFC_API extern int PORT_FlowControl (UINT16 handle, BOOLEAN enable); + + +/******************************************************************************* +** +** Function PORT_GetModemStatus +** +** Description This function retrieves modem control signals. Normally +** application will call this function after a callback +** function is called with notification that one of signals +** has been changed. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** p_signal - specify the pointer to control signals info +** +*******************************************************************************/ +#define PORT_DTRDSR_ON 0x01 +#define PORT_CTSRTS_ON 0x02 +#define PORT_RING_ON 0x04 +#define PORT_DCD_ON 0x08 + +/* +** Define default initial local modem signals state set after connection established +*/ +#define PORT_OBEX_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_SPP_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_PPP_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON) +#define PORT_DUN_DEFAULT_SIGNAL_STATE (PORT_DTRDSR_ON | PORT_CTSRTS_ON) + +RFC_API extern int PORT_GetModemStatus (UINT16 handle, UINT8 *p_control_signal); + + +/******************************************************************************* +** +** Function PORT_ClearError +** +** Description This function retreives information about a communications +** error and reports current status of a connection. The +** function should be called when an error occures to clear +** the connection error flag and to enable additional read +** and write operations. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_errors - pointer of the variable to receive error codes +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ + +#define PORT_ERR_BREAK 0x01 /* Break condition occured on the peer device */ +#define PORT_ERR_OVERRUN 0x02 /* Overrun is reported by peer device */ +#define PORT_ERR_FRAME 0x04 /* Framing error reported by peer device */ +#define PORT_ERR_RXOVER 0x08 /* Input queue overflow occured */ +#define PORT_ERR_TXFULL 0x10 /* Output queue overflow occured */ + +typedef struct +{ +#define PORT_FLAG_CTS_HOLD 0x01 /* Tx is waiting for CTS signal */ +#define PORT_FLAG_DSR_HOLD 0x02 /* Tx is waiting for DSR signal */ +#define PORT_FLAG_RLSD_HOLD 0x04 /* Tx is waiting for RLSD signal */ + + UINT16 flags; + UINT16 in_queue_size; /* Number of bytes in the input queue */ + UINT16 out_queue_size; /* Number of bytes in the output queue */ + UINT16 mtu_size; /* peer MTU size */ +} tPORT_STATUS; + + +RFC_API extern int PORT_ClearError (UINT16 handle, UINT16 *p_errors, + tPORT_STATUS *p_status); + + +/******************************************************************************* +** +** Function PORT_SendError +** +** Description This function send a communications error to the peer device +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** errors - receive error codes +** +*******************************************************************************/ +RFC_API extern int PORT_SendError (UINT16 handle, UINT8 errors); + + +/******************************************************************************* +** +** Function PORT_GetQueueStatus +** +** Description This function reports current status of a connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +RFC_API extern int PORT_GetQueueStatus (UINT16 handle, tPORT_STATUS *p_status); + + +/******************************************************************************* +** +** Function PORT_Purge +** +** Description This function discards all the data from the output or +** input queues of the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** purge_flags - specify the action to take. +** +*******************************************************************************/ +#define PORT_PURGE_TXCLEAR 0x01 +#define PORT_PURGE_RXCLEAR 0x02 + +RFC_API extern int PORT_Purge (UINT16 handle, UINT8 purge_flags); + + +/******************************************************************************* +** +** Function PORT_Read +** +** Description This function returns the pointer to the buffer received +** from the peer device. Normally application will call this +** function after receiving PORT_EVT_RXCHAR event. +** Application calling this function is responsible to free +** buffer returned. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +RFC_API extern int PORT_Read (UINT16 handle, BT_HDR **pp_buf); + + +/******************************************************************************* +** +** Function PORT_ReadData +** +** Description Normally application will call this function after receiving +** PORT_EVT_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** callback. +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +RFC_API extern int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, + UINT16 *p_len); + + +/******************************************************************************* +** +** Function PORT_Write +** +** Description This function to send BT buffer to the peer device. +** Application should not free the buffer. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_buf - pointer to the buffer with data, +** +*******************************************************************************/ +RFC_API extern int PORT_Write (UINT16 handle, BT_HDR *p_buf); + + +/******************************************************************************* +** +** Function PORT_WriteData +** +** Description This function is called from the legacy application to +** send data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count to write +** p_len - Bytes written +** +*******************************************************************************/ +RFC_API extern int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, + UINT16 *p_len); + +/******************************************************************************* +** +** Function PORT_WriteDataCO +** +** Description Normally not GKI aware application will call this function +** to send data to the port by callout functions. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +RFC_API extern int PORT_WriteDataCO (UINT16 handle, int* p_len); + +/******************************************************************************* +** +** Function PORT_Test +** +** Description Application can call this function to send RFCOMM Test frame +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** +*******************************************************************************/ +RFC_API extern int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len); + + +/******************************************************************************* +** +** Function RFCOMM_Init +** +** Description This function is called to initialize RFCOMM layer +** +*******************************************************************************/ +RFC_API extern void RFCOMM_Init (void); + + +/******************************************************************************* +** +** Function PORT_SetTraceLevel +** +** Description This function sets the trace level for RFCOMM. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +RFC_API extern UINT8 PORT_SetTraceLevel (UINT8 new_level); + + +#ifdef __cplusplus +} +#endif + +#endif /* PORT_API_H */ diff --git a/stack/include/port_ext.h b/stack/include/port_ext.h new file mode 100644 index 0000000..e25b6d9 --- /dev/null +++ b/stack/include/port_ext.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains external definitions of Port Emulation entity unit + * + ******************************************************************************/ + +#ifndef PORTEXT_H +#define PORTEXT_H + +#include "gki.h" + +/* Port emulation entity Entry Points */ +extern void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle); +#endif diff --git a/stack/include/rfcdefs.h b/stack/include/rfcdefs.h new file mode 100644 index 0000000..dcc37bc --- /dev/null +++ b/stack/include/rfcdefs.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/**************************************************************************** + * + * This file contains definitions for the RFCOMM protocol + * + ****************************************************************************/ + +#ifndef RFCDEFS_H +#define RFCDEFS_H + +#define PORT_MAX_RFC_PORTS 31 + +/* +** If nothing is negotiated MTU should be 127 +*/ +#define RFCOMM_DEFAULT_MTU 127 + +/* +** Define used by RFCOMM TS frame types +*/ +#define RFCOMM_SABME 0x2F +#define RFCOMM_UA 0x63 +#define RFCOMM_DM 0x0F +#define RFCOMM_DISC 0x43 +#define RFCOMM_UIH 0xEF + +/* +** Defenitions for the TS control frames +*/ +#define RFCOMM_CTRL_FRAME_LEN 3 +#define RFCOMM_MIN_OFFSET 5 /* ctrl 2 , len 1 or 2 bytes, credit 1 byte */ +#define RFCOMM_DATA_OVERHEAD (RFCOMM_MIN_OFFSET + 1) /* add 1 for checksum */ + +#define RFCOMM_EA 1 +#define RFCOMM_EA_MASK 0x01 +#define RFCOMM_CR_MASK 0x02 +#define RFCOMM_SHIFT_CR 1 +#define RFCOMM_SHIFT_DLCI 2 +#define RFCOMM_SHIFT_DLCI2 6 +#define RFCOMM_PF 0x10 +#define RFCOMM_PF_MASK 0x10 +#define RFCOMM_PF_OFFSET 4 +#define RFCOMM_SHIFT_LENGTH1 1 +#define RFCOMM_SHIFT_LENGTH2 7 +#define RFCOMM_SHIFT_MX_CTRL_TYPE 2 + +#define RFCOMM_INITIATOR_CMD 1 +#define RFCOMM_INITIATOR_RSP 0 +#define RFCOMM_RESPONDER_CMD 0 +#define RFCOMM_RESPONDER_RSP 1 + +#define RFCOMM_PARSE_CTRL_FIELD(ea, cr, dlci, p_data) \ +{ \ + ea = *p_data & RFCOMM_EA; \ + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; \ + dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; \ + if (!ea) dlci += *p_data++ << RFCOMM_SHIFT_DLCI2; \ +} + +#define RFCOMM_FORMAT_CTRL_FIELD(p_data, ea, cr, dlci) \ + *p_data++ = ea | cr | (dlci << RFCOMM_SHIFT_DLCI) + +#define RFCOMM_PARSE_TYPE_FIELD(type, pf, p_data) \ +{ \ + type = *p_data & ~RFCOMM_PF_MASK; \ + pf = (*p_data++ & RFCOMM_PF_MASK) >> RFCOMM_PF_OFFSET;\ +} + +#define RFCOMM_FORMAT_TYPE_FIELD(p_data, type, pf) \ + *p_data++ = (type | (pf << RFCOMM_PF_OFFSET)) \ +{ \ + type = *p_data & ~RFCOMM_PF_MASK; \ + pf = (*p_data++ & RFCOMM_PF_MASK) >> RFCOMM_PF_OFFSET;\ +} + +#define RFCOMM_PARSE_LEN_FIELD(ea, length, p_data) \ +{ \ + ea = (*p_data & RFCOMM_EA); \ + length = (*p_data++ >> RFCOMM_SHIFT_LENGTH1); \ + if (!ea) length += (*p_data++ << RFCOMM_SHIFT_LENGTH2); \ +} + +#define RFCOMM_FRAME_IS_CMD(initiator, cr) \ + (( (initiator) && !(cr)) || (!(initiator) && (cr))) + +#define RFCOMM_FRAME_IS_RSP(initiator, cr) \ + (( (initiator) && (cr)) || (!(initiator) && !(cr))) + +#define RFCOMM_CR(initiator, is_command) \ + (( ( (initiator) && (is_command)) \ + || (!(initiator) && !(is_command))) << 1) + +#define RFCOMM_I_CR(is_command) ((is_command) ? 0x02 : 0x00) + +#define RFCOMM_MAX_DLCI 61 + +#define RFCOMM_VALID_DLCI(dlci) \ + (((dlci) == 0) || (((dlci) >= 2) && ((dlci) <= RFCOMM_MAX_DLCI))) + + +/* Port Negotiation (PN) */ +#define RFCOMM_PN_DLCI_MASK 0x3F + +#define RFCOMM_PN_FRAM_TYPE_UIH 0x00 +#define RFCOMM_PN_FRAME_TYPE_MASK 0x0F + +#define RFCOMM_PN_CONV_LAYER_MASK 0xF0 +#define RFCOMM_PN_CONV_LAYER_TYPE_1 0 +#define RFCOMM_PN_CONV_LAYER_CBFC_I 0xF0 +#define RFCOMM_PN_CONV_LAYER_CBFC_R 0xE0 + +#define RFCOMM_PN_PRIORITY_MASK 0x3F +#define RFCOMM_PN_PRIORITY_0 0 + +#define RFCOMM_PN_K_MASK 0x07 + +#define RFCOMM_T1_DSEC 0 /* None negotiable in RFCOMM */ +#define RFCOMM_N2 0 /* Number of retransmissions */ +#define RFCOMM_K 0 /* Window size */ +#define RFCOMM_K_MAX 7 /* Max value of K for credit based flow control */ + +#define RFCOMM_MSC_FC 0x02 /* Flow control*/ +#define RFCOMM_MSC_RTC 0x04 /* Ready to communicate*/ +#define RFCOMM_MSC_RTR 0x08 /* Ready to receive*/ +#define RFCOMM_MSC_IC 0x40 /* Incomming call indicator*/ +#define RFCOMM_MSC_DV 0x80 /* Data Valid*/ + +#define RFCOMM_MSC_SHIFT_BREAK 4 +#define RFCOMM_MSC_BREAK_MASK 0xF0 +#define RFCOMM_MSC_BREAK_PRESENT_MASK 0x02 + +#define RFCOMM_BAUD_RATE_2400 0x00 +#define RFCOMM_BAUD_RATE_4800 0x01 +#define RFCOMM_BAUD_RATE_7200 0x02 +#define RFCOMM_BAUD_RATE_9600 0x03 +#define RFCOMM_BAUD_RATE_19200 0x04 +#define RFCOMM_BAUD_RATE_38400 0x05 +#define RFCOMM_BAUD_RATE_57600 0x06 +#define RFCOMM_BAUD_RATE_115200 0x07 +#define RFCOMM_BAUD_RATE_230400 0x08 + +#define RFCOMM_5_BITS 0x00 +#define RFCOMM_6_BITS 0x01 +#define RFCOMM_7_BITS 0x02 +#define RFCOMM_8_BITS 0x03 + +#define RFCOMM_RPN_BITS_MASK 0x03 +#define RFCOMM_RPN_BITS_SHIFT 0 + +#define RFCOMM_ONESTOPBIT 0x00 +#define RFCOMM_ONE5STOPBITS 0x01 + +#define RFCOMM_RPN_STOP_BITS_MASK 0x01 +#define RFCOMM_RPN_STOP_BITS_SHIFT 2 + +#define RFCOMM_PARITY_NO 0x00 +#define RFCOMM_PARITY_YES 0x01 +#define RFCOMM_RPN_PARITY_MASK 0x01 +#define RFCOMM_RPN_PARITY_SHIFT 3 + +#define RFCOMM_ODD_PARITY 0x00 +#define RFCOMM_EVEN_PARITY 0x01 +#define RFCOMM_MARK_PARITY 0x02 +#define RFCOMM_SPACE_PARITY 0x03 + +#define RFCOMM_RPN_PARITY_TYPE_MASK 0x03 +#define RFCOMM_RPN_PARITY_TYPE_SHIFT 4 + +#define RFCOMM_FC_OFF 0x00 +#define RFCOMM_FC_XONXOFF_ON_INPUT 0x01 +#define RFCOMM_FC_XONXOFF_ON_OUTPUT 0x02 +#define RFCOMM_FC_RTR_ON_INPUT 0x04 +#define RFCOMM_FC_RTR_ON_OUTPUT 0x08 +#define RFCOMM_FC_RTC_ON_INPUT 0x10 +#define RFCOMM_FC_RTC_ON_OUTPUT 0x20 +#define RFCOMM_FC_MASK 0x3F + +#define RFCOMM_RPN_PM_BIT_RATE 0x0001 +#define RFCOMM_RPN_PM_DATA_BITS 0x0002 +#define RFCOMM_RPN_PM_STOP_BITS 0x0004 +#define RFCOMM_RPN_PM_PARITY 0x0008 +#define RFCOMM_RPN_PM_PARITY_TYPE 0x0010 +#define RFCOMM_RPN_PM_XON_CHAR 0x0020 +#define RFCOMM_RPN_PM_XOFF_CHAR 0x0040 +#define RFCOMM_RPN_PM_XONXOFF_ON_INPUT 0x0100 +#define RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT 0x0200 +#define RFCOMM_RPN_PM_RTR_ON_INPUT 0x0400 +#define RFCOMM_RPN_PM_RTR_ON_OUTPUT 0x0800 +#define RFCOMM_RPN_PM_RTC_ON_INPUT 0x1000 +#define RFCOMM_RPN_PM_RTC_ON_OUTPUT 0x2000 +#define RFCOMM_RPN_PM_MASK 0x3F7F + +#define RFCOMM_RLS_ERROR 0x01 +#define RFCOMM_RLS_OVERRUN 0x02 +#define RFCOMM_RLS_PARITY 0x04 +#define RFCOMM_RLS_FRAMING 0x08 + +/* Multiplexor channel uses DLCI 0 */ +#define RFCOMM_MX_DLCI 0 + +/* +** Define RFCOMM Multiplexer message types +*/ +#define RFCOMM_MX_PN 0x80 +#define RFCOMM_MX_PN_LEN 8 + +#define RFCOMM_MX_CLD 0xC0 +#define RFCOMM_MX_CLD_LEN 0 + +#define RFCOMM_MX_TEST 0x20 + +#define RFCOMM_MX_FCON 0xA0 +#define RFCOMM_MX_FCON_LEN 0 + +#define RFCOMM_MX_FCOFF 0x60 +#define RFCOMM_MX_FCOFF_LEN 0 + +#define RFCOMM_MX_MSC 0xE0 +#define RFCOMM_MX_MSC_LEN_NO_BREAK 2 +#define RFCOMM_MX_MSC_LEN_WITH_BREAK 3 + +#define RFCOMM_MX_NSC 0x10 +#define RFCOMM_MX_NSC_LEN 1 + +#define RFCOMM_MX_RPN 0x90 +#define RFCOMM_MX_RPN_REQ_LEN 1 +#define RFCOMM_MX_RPN_LEN 8 + +#define RFCOMM_MX_RLS 0x50 +#define RFCOMM_MX_RLS_LEN 2 +#endif diff --git a/stack/include/sdp_api.h b/stack/include/sdp_api.h new file mode 100644 index 0000000..dcc813e --- /dev/null +++ b/stack/include/sdp_api.h @@ -0,0 +1,756 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef SDP_API_H +#define SDP_API_H + +#include "bt_target.h" +#include "sdpdefs.h" + +/***************************************************************************** +** Constants +*****************************************************************************/ + +/* Success code and error codes */ +#define SDP_SUCCESS 0x0000 +#define SDP_INVALID_VERSION 0x0001 +#define SDP_INVALID_SERV_REC_HDL 0x0002 +#define SDP_INVALID_REQ_SYNTAX 0x0003 +#define SDP_INVALID_PDU_SIZE 0x0004 +#define SDP_INVALID_CONT_STATE 0x0005 +#define SDP_NO_RESOURCES 0x0006 +#define SDP_DI_REG_FAILED 0x0007 +#define SDP_DI_DISC_FAILED 0x0008 +#define SDP_NO_DI_RECORD_FOUND 0x0009 +#define SDP_ERR_ATTR_NOT_PRESENT 0x000A +#define SDP_ILLEGAL_PARAMETER 0x000B + +#define SDP_NO_RECS_MATCH 0xFFF0 +#define SDP_CONN_FAILED 0xFFF1 +#define SDP_CFG_FAILED 0xFFF2 +#define SDP_GENERIC_ERROR 0xFFF3 +#define SDP_DB_FULL 0xFFF4 +#define SDP_INVALID_PDU 0xFFF5 +#define SDP_SECURITY_ERR 0xFFF6 +#define SDP_CONN_REJECTED 0xFFF7 +#define SDP_CANCEL 0xFFF8 + +/* these result codes are used only when SDP_FOR_JV_INCLUDED==TRUE */ +#define SDP_EVT_OPEN 0x00F0 /* connected */ +#define SDP_EVT_DATA_IND 0x00F1 /* data ind */ +#define SDP_EVT_CLOSE 0x00F2 /* disconnected */ + +/* Define the PSM that SDP uses */ +#define SDP_PSM 0x0001 + +/* Legacy #define to avoid code changes - SDP UUID is same as BT UUID */ +#define tSDP_UUID tBT_UUID + +/* Masks for attr_value field of tSDP_DISC_ATTR */ +#define SDP_DISC_ATTR_LEN_MASK 0x0FFF +#define SDP_DISC_ATTR_TYPE(len_type) (len_type >> 12) +#define SDP_DISC_ATTR_LEN(len_type) (len_type & SDP_DISC_ATTR_LEN_MASK) + +/* Maximum number of protocol list items (list_elem in tSDP_PROTOCOL_ELEM) */ +#define SDP_MAX_LIST_ELEMS 3 + + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Define a callback function for when discovery is complete. */ +typedef void (tSDP_DISC_CMPL_CB) (UINT16 result); +typedef void (tSDP_DISC_CMPL_CB2) (UINT16 result, void* user_data); + +typedef struct +{ + BD_ADDR peer_addr; + UINT16 peer_mtu; +} tSDP_DR_OPEN; + +typedef struct +{ + UINT8 *p_data; + UINT16 data_len; +} tSDP_DR_DATA; + +typedef union +{ + tSDP_DR_OPEN open; + tSDP_DR_DATA data; +} tSDP_DATA; + +/* Define a callback function for when discovery result is received. */ +typedef void (tSDP_DISC_RES_CB) (UINT16 event, tSDP_DATA *p_data); + +/* Define a structure to hold the discovered service information. */ +typedef struct +{ + union + { + UINT8 u8; /* 8-bit integer */ + UINT16 u16; /* 16-bit integer */ + UINT32 u32; /* 32-bit integer */ + UINT8 array[4]; /* Variable length field */ + struct t_sdp_disc_attr *p_sub_attr; /* Addr of first sub-attr (list)*/ + } v; + +} tSDP_DISC_ATVAL; + +typedef struct t_sdp_disc_attr +{ + struct t_sdp_disc_attr *p_next_attr; /* Addr of next linked attr */ + UINT16 attr_id; /* Attribute ID */ + UINT16 attr_len_type; /* Length and type fields */ + tSDP_DISC_ATVAL attr_value; /* Variable length entry data */ +} tSDP_DISC_ATTR; + +typedef struct t_sdp_disc_rec +{ + tSDP_DISC_ATTR *p_first_attr; /* First attribute of record */ + struct t_sdp_disc_rec *p_next_rec; /* Addr of next linked record */ + UINT32 time_read; /* The time the record was read */ + BD_ADDR remote_bd_addr; /* Remote BD address */ +} tSDP_DISC_REC; + +typedef struct +{ + UINT32 mem_size; /* Memory size of the DB */ + UINT32 mem_free; /* Memory still available */ + tSDP_DISC_REC *p_first_rec; /* Addr of first record in DB */ + UINT16 num_uuid_filters; /* Number of UUIds to filter */ + tSDP_UUID uuid_filters[SDP_MAX_UUID_FILTERS]; /* UUIDs to filter */ + UINT16 num_attr_filters; /* Number of attribute filters */ + UINT16 attr_filters[SDP_MAX_ATTR_FILTERS]; /* Attributes to filter */ + UINT8 *p_free_mem; /* Pointer to free memory */ +#if (SDP_RAW_DATA_INCLUDED == TRUE) + UINT8 *raw_data; /* Received record from server. allocated/released by client */ + UINT32 raw_size; /* size of raw_data */ + UINT32 raw_used; /* length of raw_data used */ +#endif +}tSDP_DISCOVERY_DB; + +/* This structure is used to add protocol lists and find protocol elements */ +typedef struct +{ + UINT16 protocol_uuid; + UINT16 num_params; + UINT16 params[SDP_MAX_PROTOCOL_PARAMS]; +} tSDP_PROTOCOL_ELEM; + +typedef struct +{ + UINT16 num_elems; + tSDP_PROTOCOL_ELEM list_elem[SDP_MAX_LIST_ELEMS]; +} tSDP_PROTO_LIST_ELEM; + +/* Device Identification (DI) data structure +*/ +/* Used to set the DI record */ +typedef struct t_sdp_di_record +{ + UINT16 vendor; + UINT16 vendor_id_source; + UINT16 product; + UINT16 version; + BOOLEAN primary_record; + char client_executable_url[SDP_MAX_ATTR_LEN]; /* optional */ + char service_description[SDP_MAX_ATTR_LEN]; /* optional */ + char documentation_url[SDP_MAX_ATTR_LEN]; /* optional */ +}tSDP_DI_RECORD; + +/* Used to get the DI record */ +typedef struct t_sdp_di_get_record +{ + UINT16 spec_id; + tSDP_DI_RECORD rec; +}tSDP_DI_GET_RECORD; + + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* API into the SDP layer for service discovery. */ + +/******************************************************************************* +** +** Function SDP_InitDiscoveryDb +** +** Description This function is called to initialize a discovery database. +** +** Returns TRUE if successful, FALSE if one or more parameters are bad +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_InitDiscoveryDb (tSDP_DISCOVERY_DB *p_db, UINT32 len, + UINT16 num_uuid, + tSDP_UUID *p_uuid_list, + UINT16 num_attr, + UINT16 *p_attr_list); + +/******************************************************************************* +** +** Function SDP_CancelServiceSearch +** +** Description This function cancels an active query to an SDP server. +** +** Returns TRUE if discovery cancelled, FALSE if a matching activity is not found. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_CancelServiceSearch (tSDP_DISCOVERY_DB *p_db); + +/******************************************************************************* +** +** Function SDP_ServiceSearchRequest +** +** Description This function queries an SDP server for information. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_ServiceSearchRequest (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest2 +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function with the +** user data piggyback +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_ServiceSearchAttributeRequest2 (UINT8 *p_bd_addr, + tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB2 *p_cb, void * user_data); + +/* API of utilities to find data in the local discovery database */ + +/******************************************************************************* +** +** Function SDP_FindAttributeInDb +** +** Description This function queries an SDP database for a specific attribute. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +SDP_API extern tSDP_DISC_REC *SDP_FindAttributeInDb (tSDP_DISCOVERY_DB *p_db, + UINT16 attr_id, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function SDP_FindAttributeInRec +** +** Description This function searches an SDP discovery record for a +** specific attribute. +** +** Returns Pointer to matching attribute entry, or NULL +** +*******************************************************************************/ +SDP_API extern tSDP_DISC_ATTR *SDP_FindAttributeInRec (tSDP_DISC_REC *p_rec, + UINT16 attr_id); + + +/******************************************************************************* +** +** Function SDP_FindServiceInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +SDP_API extern tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, + UINT16 service_uuid, + tSDP_DISC_REC *p_start_rec); + + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** NOTE the only difference between this function and the previous +** function "SDP_FindServiceInDb()" is that this function takes +** a tBT_UUID input. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +SDP_API extern tSDP_DISC_REC *SDP_FindServiceUUIDInDb (tSDP_DISCOVERY_DB *p_db, + tBT_UUID *p_uuid, + tSDP_DISC_REC *p_start_rec); + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec_128bit +** +** Description This function is called to read the 128-bit service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC *p_rec, tBT_UUID * p_uuid); + +/******************************************************************************* +** +** Function SDP_FindServiceInDb_128bit +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +SDP_API extern tSDP_DISC_REC *SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_REC *p_start_rec); + +/******************************************************************************* +** +** Function SDP_FindProtocolListElemInRec +** +** Description This function looks at a specific discovery record for a +** protocol list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, + UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem); + + +/******************************************************************************* +** +** Function SDP_FindAddProtoListsElemInRec +** +** Description This function looks at a specific discovery record for a +** protocol list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_FindAddProtoListsElemInRec (tSDP_DISC_REC *p_rec, + UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem); + + +/******************************************************************************* +** +** Function SDP_FindProfileVersionInRec +** +** Description This function looks at a specific discovery record for the +** Profile list descriptor, and pulls out the version number. +** The version number consists of an 8-bit major version and +** an 8-bit minor version. +** +** Returns TRUE if found, FALSE if not +** If found, the major and minor version numbers that were passed +** in are filled in. +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, + UINT16 profile_uuid, + UINT16 *p_version); + + +/* API into SDP for local service database updates */ + +/******************************************************************************* +** +** Function SDP_CreateRecord +** +** Description This function is called to create a record in the database. +** This would be through the SDP database maintenance API. The +** record is created empty, teh application should then call +** "add_attribute" to add the record's attributes. +** +** Returns Record handle if OK, else 0. +** +*******************************************************************************/ +SDP_API extern UINT32 SDP_CreateRecord (void); + + +/******************************************************************************* +** +** Function SDP_DeleteRecord +** +** Description This function is called to add a record (or all records) +** from the database. This would be through the SDP database +** maintenance API. +** +** If a record handle of 0 is passed, all records are deleted. +** +** Returns TRUE if succeeded, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_DeleteRecord (UINT32 handle); + + +/******************************************************************************* +** +** Function SDP_ReadRecord +** +** Description This function is called to get the raw data of the record +** with the given handle from the database. +** +** Returns -1, if the record is not found. +** Otherwise, the offset (0 or 1) to start of data in p_data. +** +** The size of data copied into p_data is in *p_data_len. +** +*******************************************************************************/ +SDP_API extern INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len); + +/******************************************************************************* +** +** Function SDP_AddAttribute +** +** Description This function is called to add an attribute to a record. +** This would be through the SDP database maintenance API. +** If the attribute already exists in the record, it is replaced +** with the new value. +** +** NOTE Attribute values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddAttribute (UINT32 handle, UINT16 attr_id, + UINT8 attr_type, UINT32 attr_len, + UINT8 *p_val); + + +/******************************************************************************* +** +** Function SDP_AddSequence +** +** Description This function is called to add a sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** NOTE Element values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddSequence (UINT32 handle, UINT16 attr_id, + UINT16 num_elem, UINT8 type[], + UINT8 len[], UINT8 *p_val[]); + + +/******************************************************************************* +** +** Function SDP_AddUuidSequence +** +** Description This function is called to add a UUID sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddUuidSequence (UINT32 handle, UINT16 attr_id, + UINT16 num_uuids, UINT16 *p_uuids); + + +/******************************************************************************* +** +** Function SDP_AddProtocolList +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddProtocolList (UINT32 handle, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list); + + +/******************************************************************************* +** +** Function SDP_AddAdditionProtoLists +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddAdditionProtoLists (UINT32 handle, UINT16 num_elem, + tSDP_PROTO_LIST_ELEM *p_proto_list); + + +/******************************************************************************* +** +** Function SDP_AddProfileDescriptorList +** +** Description This function is called to add a profile descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddProfileDescriptorList (UINT32 handle, + UINT16 profile_uuid, + UINT16 version); + + +/******************************************************************************* +** +** Function SDP_AddLanguageBaseAttrIDList +** +** Description This function is called to add a language base attr list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddLanguageBaseAttrIDList (UINT32 handle, + UINT16 lang, UINT16 char_enc, + UINT16 base_id); + + +/******************************************************************************* +** +** Function SDP_AddServiceClassIdList +** +** Description This function is called to add a service list to a record. +** This would be through the SDP database maintenance API. +** If the service list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_AddServiceClassIdList (UINT32 handle, + UINT16 num_services, + UINT16 *p_service_uuids); + + +/******************************************************************************* +** +** Function SDP_DeleteAttribute +** +** Description This function is called to delete an attribute from a record. +** This would be through the SDP database maintenance API. +** +** Returns TRUE if deleted OK, else FALSE if not found +** +*******************************************************************************/ +SDP_API extern BOOLEAN SDP_DeleteAttribute (UINT32 handle, UINT16 attr_id); + + +/* Device Identification APIs */ + +/******************************************************************************* +** +** Function SDP_SetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Returns Returns SDP_SUCCESS if record added successfully, else error +** +*******************************************************************************/ +SDP_API extern UINT16 SDP_SetLocalDiRecord (tSDP_DI_RECORD *device_info, + UINT32 *p_handle); + +/******************************************************************************* +** +** Function SDP_GetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Fills in the device information of the record +** p_handle - if p_handle == NULL, the primary record is returned +** +** Returns Returns SDP_SUCCESS if record exists, else error +** +*******************************************************************************/ +SDP_API extern UINT16 SDP_GetLocalDiRecord(tSDP_DI_GET_RECORD *p_device_info, + UINT32 *p_handle ); + +/******************************************************************************* +** +** Function SDP_DiDiscover +** +** Description This function queries a remote device for DI information. +** +** Returns SDP_SUCCESS if query started successfully, else error +** +*******************************************************************************/ +SDP_API extern UINT16 SDP_DiDiscover (BD_ADDR remote_device, + tSDP_DISCOVERY_DB *p_db, UINT32 len, + tSDP_DISC_CMPL_CB *p_cb); + + +/******************************************************************************* +** +** Function SDP_GetNumDiRecords +** +** Description Searches specified database for DI records +** +** Returns number of DI records found +** +*******************************************************************************/ +SDP_API extern UINT8 SDP_GetNumDiRecords (tSDP_DISCOVERY_DB *p_db); + + +/******************************************************************************* +** +** Function SDP_GetDiRecord +** +** Description This function retrieves a remote device's DI record from +** the specified database. +** +** Returns SDP_SUCCESS if record retrieved, else error +** +*******************************************************************************/ +SDP_API extern UINT16 SDP_GetDiRecord (UINT8 getRecordIndex, + tSDP_DI_GET_RECORD *device_info, + tSDP_DISCOVERY_DB *p_db); + + +/******************************************************************************* +** +** Function SDP_SetTraceLevel +** +** Description This function sets the trace level for SDP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +SDP_API extern UINT8 SDP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function SDP_ConnOpen +** +** Description This function creates a connection to the SDP server on the +** given device. +** +** Returns 0, if failed to initiate connection. Otherwise, the handle. +** +*******************************************************************************/ +SDP_API UINT32 SDP_ConnOpen (UINT8 *p_bd_addr, tSDP_DISC_RES_CB *p_rcb, + tSDP_DISC_CMPL_CB *p_cb); + +/******************************************************************************* +** +** Function SDP_WriteData +** +** Description This function sends data to the connected SDP server. +** +** Returns TRUE if data is sent, FALSE if failed. +** +*******************************************************************************/ +SDP_API BOOLEAN SDP_WriteData (UINT32 handle, BT_HDR *p_msg); + +/******************************************************************************* +** +** Function SDP_ConnClose +** +** Description This function is called to close a SDP connection. +** +** Parameters: handle - Handle of the connection returned by SDP_ConnOpen +** +** Returns TRUE if connection is closed, FALSE if failed to find the handle. +** +*******************************************************************************/ +SDP_API BOOLEAN SDP_ConnClose (UINT32 handle); + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec +** +** Description This function is called to read the service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +SDP_API BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid); + +#ifdef __cplusplus +} +#endif + +#endif /* SDP_API_H */ diff --git a/stack/include/sdpdefs.h b/stack/include/sdpdefs.h new file mode 100644 index 0000000..c290e03 --- /dev/null +++ b/stack/include/sdpdefs.h @@ -0,0 +1,319 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the definitions for the SDP API + * + ******************************************************************************/ + +#ifndef SDP_DEFS_H +#define SDP_DEFS_H + +/* Define the service attribute IDs. +*/ +#define ATTR_ID_SERVICE_RECORD_HDL 0x0000 +#define ATTR_ID_SERVICE_CLASS_ID_LIST 0x0001 +#define ATTR_ID_SERVICE_RECORD_STATE 0x0002 +#define ATTR_ID_SERVICE_ID 0x0003 +#define ATTR_ID_PROTOCOL_DESC_LIST 0x0004 +#define ATTR_ID_BROWSE_GROUP_LIST 0x0005 +#define ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST 0x0006 +#define ATTR_ID_SERVICE_INFO_TIME_TO_LIVE 0x0007 +#define ATTR_ID_SERVICE_AVAILABILITY 0x0008 +#define ATTR_ID_BT_PROFILE_DESC_LIST 0x0009 +#define ATTR_ID_DOCUMENTATION_URL 0x000A +#define ATTR_ID_CLIENT_EXE_URL 0x000B +#define ATTR_ID_ICON_URL 0x000C +#define ATTR_ID_ADDITION_PROTO_DESC_LISTS 0x000D + +#define LANGUAGE_BASE_ID 0x0100 +#define ATTR_ID_SERVICE_NAME LANGUAGE_BASE_ID + 0x0000 +#define ATTR_ID_SERVICE_DESCRIPTION LANGUAGE_BASE_ID + 0x0001 +#define ATTR_ID_PROVIDER_NAME LANGUAGE_BASE_ID + 0x0002 + +/* Device Identification (DI) +*/ +#define ATTR_ID_SPECIFICATION_ID 0x0200 +#define ATTR_ID_VENDOR_ID 0x0201 +#define ATTR_ID_PRODUCT_ID 0x0202 +#define ATTR_ID_PRODUCT_VERSION 0x0203 +#define ATTR_ID_PRIMARY_RECORD 0x0204 +#define ATTR_ID_VENDOR_ID_SOURCE 0x0205 + +#define BLUETOOTH_DI_SPECIFICATION 0x0103 /* 1.3 */ +#define DI_VENDOR_ID_DEFAULT 0xFFFF +#define DI_VENDOR_ID_SOURCE_BTSIG 0x0001 +#define DI_VENDOR_ID_SOURCE_USBIF 0x0002 + + +#define ATTR_ID_IP_SUBNET 0x0200 /* PAN Profile (***) */ +#define ATTR_ID_VERSION_NUMBER_LIST 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_EXTERNAL_NETWORK 0x0301 +#define ATTR_ID_FAX_CLASS_1_SUPPORT 0x0302 +#define ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 +#define ATTR_ID_DEVICE_NAME 0x0302 +#define ATTR_ID_SUPPORTED_FORMATS_LIST 0x0303 +#define ATTR_ID_FAX_CLASS_2_0_SUPPORT 0x0303 +#define ATTR_ID_FAX_CLASS_2_SUPPORT 0x0304 +#define ATTR_ID_FRIENDLY_NAME 0x0304 +#define ATTR_ID_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define ATTR_ID_NETWORK_ADDRESS 0x0306 +#define ATTR_ID_DEVICE_LOCATION 0x0306 +#define ATTR_ID_WAP_GATEWAY 0x0307 +#define ATTR_ID_HOME_PAGE_URL 0x0308 +#define ATTR_ID_WAP_STACK_TYPE 0x0309 +#define ATTR_ID_IMG_SUPPORTED_CAPABILITIES 0x0310 /* Imaging Profile */ +#define ATTR_ID_SUPPORTED_FEATURES 0x0311 /* HFP, BIP */ +#define ATTR_ID_IMG_SUPPORTED_FUNCTIONS 0x0312 /* Imaging Profile */ +#define ATTR_ID_IMG_TOT_DATA_CAPABILITY 0x0313 /* Imaging Profile */ +#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 */ + +/* These values are for the BPP profile */ +#define ATTR_ID_DOCUMENT_FORMATS_SUPPORTED 0x0350 +#define ATTR_ID_CHARACTER_REPERTOIRES_SUPPORTED 0x0352 +#define ATTR_ID_XHTML_IMAGE_FORMATS_SUPPORTED 0x0354 +#define ATTR_ID_COLOR_SUPPORTED 0x0356 +#define ATTR_ID_1284ID 0x0358 +#define ATTR_ID_PRINTER_NAME 0x035A +#define ATTR_ID_PRINTER_LOCATION 0x035C +#define ATTR_ID_DUPLEX_SUPPORTED 0x035E +#define ATTR_ID_MEDIA_TYPES_SUPPORTED 0x0360 +#define ATTR_ID_MAX_MEDIA_WIDTH 0x0362 +#define ATTR_ID_MAX_MEDIA_LENGTH 0x0364 +#define ATTR_ID_ENHANCED_LAYOUT_SUPPORTED 0x0366 +#define ATTR_ID_RUI_FORMATS_SUPPORTED 0x0368 +#define ATTR_ID_RUI_REF_PRINTING_SUPPORTED 0x0370 /* Boolean */ +#define ATTR_ID_RUI_DIRECT_PRINTING_SUPPORTED 0x0372 /* Boolean */ +#define ATTR_ID_REF_PRINTING_TOP_URL 0x0374 +#define ATTR_ID_DIRECT_PRINTING_TOP_URL 0x0376 +#define ATTR_ID_PRINTER_ADMIN_RUI_TOP_URL 0x0378 +#define ATTR_ID_BPP_DEVICE_NAME 0x037A + +/* These values are for the PAN profile */ +#define ATTR_ID_SECURITY_DESCRIPTION 0x030A +#define ATTR_ID_NET_ACCESS_TYPE 0x030B +#define ATTR_ID_MAX_NET_ACCESS_RATE 0x030C +#define ATTR_ID_IPV4_SUBNET 0x030D +#define ATTR_ID_IPV6_SUBNET 0x030E +#define ATTR_ID_PAN_SECURITY 0x0400 + +/* These values are for HID profile */ +#define ATTR_ID_HID_DEVICE_RELNUM 0x0200 +#define ATTR_ID_HID_PARSER_VERSION 0x0201 +#define ATTR_ID_HID_DEVICE_SUBCLASS 0x0202 +#define ATTR_ID_HID_COUNTRY_CODE 0x0203 +#define ATTR_ID_HID_VIRTUAL_CABLE 0x0204 +#define ATTR_ID_HID_RECONNECT_INITIATE 0x0205 +#define ATTR_ID_HID_DESCRIPTOR_LIST 0x0206 +#define ATTR_ID_HID_LANGUAGE_ID_BASE 0x0207 +#define ATTR_ID_HID_SDP_DISABLE 0x0208 +#define ATTR_ID_HID_BATTERY_POWER 0x0209 +#define ATTR_ID_HID_REMOTE_WAKE 0x020A +#define ATTR_ID_HID_PROFILE_VERSION 0x020B +#define ATTR_ID_HID_LINK_SUPERVISION_TO 0x020C +#define ATTR_ID_HID_NORMALLY_CONNECTABLE 0x020D +#define ATTR_ID_HID_BOOT_DEVICE 0x020E +#define ATTR_ID_HID_SSR_HOST_MAX_LAT 0x020F +#define ATTR_ID_HID_SSR_HOST_MIN_TOUT 0x0210 + +/* These values are for the HDP profile */ +#define ATTR_ID_HDP_SUP_FEAT_LIST 0x0200 /* Supported features list */ +#define ATTR_ID_HDP_DATA_EXCH_SPEC 0x0301 /* Data exchange specification */ +#define ATTR_ID_HDP_MCAP_SUP_PROC 0x0302 /* MCAP supported procedures */ + +/* a temporary value for IOP */ +#ifndef ATTR_ID_OBX_OVR_L2CAP_PSM +#define ATTR_ID_OBX_OVR_L2CAP_PSM 0x0200 +#endif + + +/* Define common 16-bit protocol UUIDs +*/ +#define UUID_PROTOCOL_SDP 0x0001 +#define UUID_PROTOCOL_UDP 0x0002 +#define UUID_PROTOCOL_RFCOMM 0x0003 +#define UUID_PROTOCOL_TCP 0x0004 +#define UUID_PROTOCOL_TCS_BIN 0x0005 +#define UUID_PROTOCOL_TCS_AT 0x0006 +#define UUID_PROTOCOL_OBEX 0x0008 +#define UUID_PROTOCOL_IP 0x0009 +#define UUID_PROTOCOL_FTP 0x000A +#define UUID_PROTOCOL_HTTP 0x000C +#define UUID_PROTOCOL_WSP 0x000E +#define UUID_PROTOCOL_BNEP 0x000F +#define UUID_PROTOCOL_UPNP 0x0010 +#define UUID_PROTOCOL_HIDP 0x0011 +#define UUID_PROTOCOL_HCRP_CTRL 0x0012 +#define UUID_PROTOCOL_HCRP_DATA 0x0014 +#define UUID_PROTOCOL_HCRP_NOTIF 0x0016 +#define UUID_PROTOCOL_AVCTP 0x0017 +#define UUID_PROTOCOL_AVDTP 0x0019 +#define UUID_PROTOCOL_CMTP 0x001B +#define UUID_PROTOCOL_UDI 0x001D +#define UUID_PROTOCOL_MCAP_CTRL 0x001E +#define UUID_PROTOCOL_MCAP_DATA 0x001F +#define UUID_PROTOCOL_L2CAP 0x0100 +#define UUID_PROTOCOL_ATT 0x0007 + +/* Define common 16-bit service class UUIDs +*/ +#define UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER 0X1000 +#define UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR 0X1001 +#define UUID_SERVCLASS_PUBLIC_BROWSE_GROUP 0X1002 +#define UUID_SERVCLASS_SERIAL_PORT 0X1101 +#define UUID_SERVCLASS_LAN_ACCESS_USING_PPP 0X1102 +#define UUID_SERVCLASS_DIALUP_NETWORKING 0X1103 +#define UUID_SERVCLASS_IRMC_SYNC 0X1104 +#define UUID_SERVCLASS_OBEX_OBJECT_PUSH 0X1105 +#define UUID_SERVCLASS_OBEX_FILE_TRANSFER 0X1106 +#define UUID_SERVCLASS_IRMC_SYNC_COMMAND 0X1107 +#define UUID_SERVCLASS_HEADSET 0X1108 +#define UUID_SERVCLASS_CORDLESS_TELEPHONY 0X1109 +#define UUID_SERVCLASS_AUDIO_SOURCE 0X110A +#define UUID_SERVCLASS_AUDIO_SINK 0X110B +#define UUID_SERVCLASS_AV_REM_CTRL_TARGET 0X110C /* Audio/Video Control profile */ +#define UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION 0X110D /* Advanced Audio Distribution profile */ +#define UUID_SERVCLASS_AV_REMOTE_CONTROL 0X110E /* Audio/Video Control profile */ +#define UUID_SERVCLASS_AV_REM_CTRL_CONTROL 0X110F /* Audio/Video Control profile */ +#define UUID_SERVCLASS_INTERCOM 0X1110 +#define UUID_SERVCLASS_FAX 0X1111 +#define UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY 0X1112 +#define UUID_SERVCLASS_WAP 0X1113 +#define UUID_SERVCLASS_WAP_CLIENT 0X1114 +#define UUID_SERVCLASS_PANU 0X1115 /* PAN profile */ +#define UUID_SERVCLASS_NAP 0X1116 /* PAN profile */ +#define UUID_SERVCLASS_GN 0X1117 /* PAN profile */ +#define UUID_SERVCLASS_DIRECT_PRINTING 0X1118 /* BPP profile */ +#define UUID_SERVCLASS_REFERENCE_PRINTING 0X1119 /* BPP profile */ +#define UUID_SERVCLASS_IMAGING 0X111A /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_RESPONDER 0X111B /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE 0X111C /* Imaging profile */ +#define UUID_SERVCLASS_IMAGING_REF_OBJECTS 0X111D /* Imaging profile */ +#define UUID_SERVCLASS_HF_HANDSFREE 0X111E /* Handsfree profile */ +#define UUID_SERVCLASS_AG_HANDSFREE 0X111F /* Handsfree profile */ +#define UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE 0X1120 /* BPP profile */ +#define UUID_SERVCLASS_REFLECTED_UI 0X1121 /* BPP profile */ +#define UUID_SERVCLASS_BASIC_PRINTING 0X1122 /* BPP profile */ +#define UUID_SERVCLASS_PRINTING_STATUS 0X1123 /* BPP profile */ +#define UUID_SERVCLASS_HUMAN_INTERFACE 0X1124 /* HID profile */ +#define UUID_SERVCLASS_CABLE_REPLACEMENT 0X1125 /* HCRP profile */ +#define UUID_SERVCLASS_HCRP_PRINT 0X1126 /* HCRP profile */ +#define UUID_SERVCLASS_HCRP_SCAN 0X1127 /* HCRP profile */ +#define UUID_SERVCLASS_COMMON_ISDN_ACCESS 0X1128 /* CAPI Message Transport Protocol*/ +#define UUID_SERVCLASS_VIDEO_CONFERENCING_GW 0X1129 /* Video Conferencing profile */ +#define UUID_SERVCLASS_UDI_MT 0X112A /* Unrestricted Digital Information profile */ +#define UUID_SERVCLASS_UDI_TA 0X112B /* Unrestricted Digital Information profile */ +#define UUID_SERVCLASS_VCP 0X112C /* Video Conferencing profile */ +#define UUID_SERVCLASS_SAP 0X112D /* SIM Access profile */ +#define UUID_SERVCLASS_PBAP_PCE 0X112E /* Phonebook Access - PCE */ +#define UUID_SERVCLASS_PBAP_PSE 0X112F /* Phonebook Access - PSE */ +#define UUID_SERVCLASS_PHONE_ACCESS 0x1130 +#define UUID_SERVCLASS_HEADSET_HS 0x1131 /* Headset - HS, from HSP v1.2 */ +#define UUID_SERVCLASS_PNP_INFORMATION 0X1200 /* Device Identification */ +#define UUID_SERVCLASS_GENERIC_NETWORKING 0X1201 +#define UUID_SERVCLASS_GENERIC_FILETRANSFER 0X1202 +#define UUID_SERVCLASS_GENERIC_AUDIO 0X1203 +#define UUID_SERVCLASS_GENERIC_TELEPHONY 0X1204 +#define UUID_SERVCLASS_UPNP_SERVICE 0X1205 /* UPNP_Service [ESDP] */ +#define UUID_SERVCLASS_UPNP_IP_SERVICE 0X1206 /* UPNP_IP_Service [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_PAN 0X1300 /* UPNP_IP_PAN [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_LAP 0X1301 /* UPNP_IP_LAP [ESDP] */ +#define UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP 0X1302 /* UPNP_L2CAP [ESDP] */ +#define UUID_SERVCLASS_VIDEO_SOURCE 0X1303 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_VIDEO_SINK 0X1304 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_VIDEO_DISTRIBUTION 0X1305 /* Video Distribution Profile (VDP) */ +#define UUID_SERVCLASS_HDP_PROFILE 0X1400 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_HDP_SOURCE 0X1401 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_HDP_SINK 0X1402 /* Health Device profile (HDP) */ +#define UUID_SERVCLASS_MAP_PROFILE 0X1134 /* MAP profile UUID */ +#define UUID_SERVCLASS_MESSAGE_ACCESS 0X1132 /* Message Access Service UUID */ +#define UUID_SERVCLASS_MESSAGE_NOTIFICATION 0X1133 /* Message Notification Service UUID */ + +#define UUID_SERVCLASS_GAP_SERVER 0x1800 +#define UUID_SERVCLASS_GATT_SERVER 0x1801 +#define UUID_SERVCLASS_IMMEDIATE_ALERT 0x1802 /* immediate alert */ +#define UUID_SERVCLASS_LINKLOSS 0x1803 /* Link Loss Alert */ +#define UUID_SERVCLASS_TX_POWER 0x1804 /* TX power */ +#define UUID_SERVCLASS_CURRENT_TIME 0x1805 /* Link Loss Alert */ +#define UUID_SERVCLASS_DST_CHG 0x1806 /* DST Time change */ +#define UUID_SERVCLASS_REF_TIME_UPD 0x1807 /* reference time update */ +#define UUID_SERVCLASS_DEVICE_INFO 0x180A /* device info service */ +#define UUID_SERVCLASS_NWA 0x180B /* Network availability */ +#define UUID_SERVCLASS_PHALERT 0x180C /* phone alert service */ +#define UUID_SERVCLASS_GLUCOSE 0xC000 /* Glucose Meter Service */ +#define UUID_SERVCLASS_TEST_SERVER 0x9000 /* Test Group UUID */ + +#if (BTM_WBS_INCLUDED == TRUE ) +#define UUID_CODEC_CVSD 0x0001 /* CVSD */ +#define UUID_CODEC_MSBC 0x0002 /* mSBC */ +#endif + +/* Define all the 'Descriptor Type' values. +*/ +#define NULL_DESC_TYPE 0 +#define UINT_DESC_TYPE 1 +#define TWO_COMP_INT_DESC_TYPE 2 +#define UUID_DESC_TYPE 3 +#define TEXT_STR_DESC_TYPE 4 +#define BOOLEAN_DESC_TYPE 5 +#define DATA_ELE_SEQ_DESC_TYPE 6 +#define DATA_ELE_ALT_DESC_TYPE 7 +#define URL_DESC_TYPE 8 + +/* Define all the "Descriptor Size" values. +*/ +#define SIZE_ONE_BYTE 0 +#define SIZE_TWO_BYTES 1 +#define SIZE_FOUR_BYTES 2 +#define SIZE_EIGHT_BYTES 3 +#define SIZE_SIXTEEN_BYTES 4 +#define SIZE_IN_NEXT_BYTE 5 +#define SIZE_IN_NEXT_WORD 6 +#define SIZE_IN_NEXT_LONG 7 + +/* Language Encoding Constants */ +#define LANG_ID_CODE_ENGLISH ((UINT16) 0x656e) /* "en" */ +#define LANG_ID_CHAR_ENCODE_UTF8 ((UINT16) 0x006a) /* UTF-8 */ + +/* Constants used for display purposes only. These define ovelapping attribute values */ +#define ATTR_ID_VERS_OR_GRP_OR_DRELNUM_OR_IPSUB_OR_SPECID 0x0200 +#define ATTR_ID_VEND_ID_OR_SERVICE_DB_STATE_OR_PARSE_VER 0x0201 +#define ATTR_ID_PROD_ID_OR_HID_DEV_SUBCLASS 0x0202 +#define ATTR_ID_PROD_VER_OR_HID_COUNTRY_CODE 0x0203 +#define ATTR_ID_PRIMARY_REC_OR_HID_VIRTUAL_CABLE 0x0204 +#define ATTR_ID_DI_VENDOR_ID_SOURCE_OR_HID_INIT_RECONNECT 0x0205 +#define ATTR_ID_SERV_VERS_OR_1284ID 0x0300 +#define ATTR_ID_DATA_STORES_OR_NETWORK 0x0301 +#define ATTR_ID_FAX_1_OR_AUD_VOL_OR_DEV_NAME 0x0302 +#define ATTR_ID_FORMATS_OR_FAX_2_0 0x0303 +#define ATTR_ID_FAX_CLASS_2_OR_FRIENDLY_NAME 0x0304 +#define ATTR_ID_NETADDRESS_OR_DEVLOCATION 0x0306 + +#endif + + diff --git a/stack/include/smp_api.h b/stack/include/smp_api.h new file mode 100644 index 0000000..bd7b131 --- /dev/null +++ b/stack/include/smp_api.h @@ -0,0 +1,300 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the SMP API function external definitions. + * + ******************************************************************************/ +#ifndef SMP_API_H +#define SMP_API_H + +#include "bt_target.h" + +#define SMP_PIN_CODE_LEN_MAX PIN_CODE_LEN +#define SMP_PIN_CODE_LEN_MIN 6 + +/* SMP event type */ +#define SMP_IO_CAP_REQ_EVT 1 /* IO capability request event */ +#define SMP_SEC_REQUEST_EVT 2 /* SMP pairing request */ +#define SMP_PASSKEY_NOTIF_EVT 3 /* passkey notification event */ +#define SMP_PASSKEY_REQ_EVT 4 /* passkey request event */ +#define SMP_OOB_REQ_EVT 5 /* OOB request event */ +#define SMP_COMPLT_EVT 6 /* SMP complete event */ +typedef UINT8 tSMP_EVT; + + +/* pairing failure reason code */ +#define SMP_PASSKEY_ENTRY_FAIL 0x01 +#define SMP_OOB_FAIL 0x02 +#define SMP_PAIR_AUTH_FAIL 0x03 +#define SMP_CONFIRM_VALUE_ERR 0x04 +#define SMP_PAIR_NOT_SUPPORT 0x05 +#define SMP_ENC_KEY_SIZE 0x06 +#define SMP_INVALID_CMD 0x07 +#define SMP_PAIR_FAIL_UNKNOWN 0x08 +#define SMP_REPEATED_ATTEMPTS 0x09 +#define SMP_PAIR_FAILURE_MAX SMP_REPEATED_ATTEMPTS +/* self defined error code */ +#define SMP_PAIR_INTERNAL_ERR 0x0A +#define SMP_UNKNOWN_IO_CAP 0x0B /* unknown IO capability, unable to decide associatino model */ +#define SMP_INIT_FAIL 0x0C +#define SMP_CONFIRM_FAIL 0x0D +#define SMP_BUSY 0x0E +#define SMP_ENC_FAIL 0x0F +#define SMP_STARTED 0x10 +#define SMP_RSP_TIMEOUT 0x11 +#define SMP_DIV_NOT_AVAIL 0x12 +#define SMP_FAIL 0x13 /* unspecified failed reason */ +#define SMP_SUCCESS 0 + +typedef UINT8 tSMP_STATUS; + + +/* Device IO capability */ +#define SMP_IO_CAP_OUT BTM_IO_CAP_OUT /* DisplayOnly */ +#define SMP_IO_CAP_IO BTM_IO_CAP_IO /* DisplayYesNo */ +#define SMP_IO_CAP_IN BTM_IO_CAP_IN /* KeyboardOnly */ +#define SMP_IO_CAP_NONE BTM_IO_CAP_NONE /* NoInputNoOutput */ +#define SMP_IO_CAP_KBDISP BTM_IO_CAP_KBDISP /* Keyboard Display */ +#define SMP_IO_CAP_MAX BTM_IO_CAP_MAX +typedef UINT8 tSMP_IO_CAP; + +#ifndef SMP_DEFAULT_IO_CAPS + #define SMP_DEFAULT_IO_CAPS SMP_IO_CAP_KBDISP +#endif + +/* OOB data present or not */ +enum +{ + SMP_OOB_NONE, + SMP_OOB_PRESENT, + SMP_OOB_UNKNOWN +}; +typedef UINT8 tSMP_OOB_FLAG; + +#define SMP_AUTH_NO_BOND 0x00 +#define SMP_AUTH_GEN_BOND 0x01 //todo sdh change GEN_BOND to BOND + +/* SMP Authentication requirement */ +#define SMP_AUTH_YN_BIT (1 << 2) +#define SMP_AUTH_MASK (SMP_AUTH_GEN_BOND|SMP_AUTH_YN_BIT) + + +#define SMP_AUTH_BOND SMP_AUTH_GEN_BOND + +#define SMP_AUTH_NB_ENC_ONLY 0x00 //(SMP_AUTH_MASK | BTM_AUTH_SP_NO) /* no MITM, No Bonding, Encryptino only */ +#define SMP_AUTH_NB_IOCAP (SMP_AUTH_NO_BOND | SMP_AUTH_YN_BIT) /* MITM, No Bonding, Use IO Capability + to detrermine authenticaion procedure */ +#define SMP_AUTH_GB_ENC_ONLY (SMP_AUTH_GEN_BOND ) /* no MITM, General Bonding, Encryptino only */ +#define SMP_AUTH_GB_IOCAP (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT) /* MITM, General Bonding, Use IO Capability + to detrermine authenticaion procedure */ +typedef UINT8 tSMP_AUTH_REQ; + +#define SMP_SEC_NONE 0 +#define SMP_SEC_UNAUTHENTICATE (1 << 0) +#define SMP_SEC_AUTHENTICATED (1 << 2) +typedef UINT8 tSMP_SEC_LEVEL; + +/* SMP key types */ +#define SMP_SEC_KEY_TYPE_ENC (1 << 0) /* encryption key */ +#define SMP_SEC_KEY_TYPE_ID (1 << 1) /* identity key */ +#define SMP_SEC_KEY_TYPE_CSRK (1 << 2) /* slave CSRK */ +typedef UINT8 tSMP_KEYS; + +/* default security key distribution value */ +#define SMP_SEC_DEFAULT_KEY (SMP_SEC_KEY_TYPE_ENC | SMP_SEC_KEY_TYPE_ID | SMP_SEC_KEY_TYPE_CSRK) + +/* data type for BTM_SP_IO_REQ_EVT */ +typedef struct +{ + tSMP_IO_CAP io_cap; /* local IO capabilities */ + tSMP_OOB_FLAG oob_data; /* OOB data present (locally) for the peer device */ + tSMP_AUTH_REQ auth_req; /* Authentication required (for local device) */ + UINT8 max_key_size; /* max encryption key size */ + tSMP_KEYS init_keys; /* initiator keys to be distributed */ + tSMP_KEYS resp_keys; /* responder keys */ +} tSMP_IO_REQ; + +typedef struct +{ + UINT8 reason; + UINT8 sec_level; + BOOLEAN is_pair_cancel; +} tSMP_CMPL; + +typedef union +{ + UINT32 passkey; + tSMP_IO_REQ io_req; /* IO request */ + tSMP_CMPL cmplt; + +}tSMP_EVT_DATA; + + +/* AES Encryption output */ +typedef struct +{ + UINT8 status; + UINT8 param_len; + UINT16 opcode; + UINT8 param_buf[BT_OCTET16_LEN]; +} tSMP_ENC; + +/* Simple Pairing Events. Called by the stack when Simple Pairing related +** events occur. +*/ +typedef UINT8 (tSMP_CALLBACK) (tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); + +/* callback function for CMAC algorithm +*/ +typedef void (tCMAC_CMPL_CBACK)(UINT8 *p_mac, UINT16 tlen, UINT32 sign_counter); + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/* API of SMP */ + +/******************************************************************************* +** +** Function SMP_Init +** +** Description This function initializes the SMP unit. +** +** Returns void +** +*******************************************************************************/ + SMP_API extern void SMP_Init(void); + +/******************************************************************************* +** +** Function SMP_SetTraceLevel +** +** Description This function sets the trace level for SMP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Returns The new or current trace level +** +*******************************************************************************/ + SMP_API extern UINT8 SMP_SetTraceLevel (UINT8 new_level); + +/******************************************************************************* +** +** Function SMP_Register +** +** Description This function register for the SMP service callback. +** +** Returns void +** +*******************************************************************************/ + SMP_API extern BOOLEAN SMP_Register (tSMP_CALLBACK *p_cback); + +/******************************************************************************* +** +** Function SMP_Pair +** +** Description This function is called to start a SMP pairing. +** +** Returns SMP_STARTED if bond started, else otherwise exception. +** +*******************************************************************************/ + SMP_API extern tSMP_STATUS SMP_Pair (BD_ADDR bd_addr); +/******************************************************************************* +** +** Function SMP_PairCancel +** +** Description This function is called to cancel a SMP pairing. +** +** Returns TRUE - pairing cancelled +** +*******************************************************************************/ + SMP_API extern BOOLEAN SMP_PairCancel (BD_ADDR bd_addr); + +/******************************************************************************* +** +** Function SMP_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation SMP_SUCCESS if success. +** Otherwise, SMP_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ + SMP_API extern void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res); + +/******************************************************************************* +** +** Function SMP_PasskeyReply +** +** Description This function is called after Security Manager submitted +** Passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ + SMP_API extern void SMP_PasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey); + +/******************************************************************************* +** +** Function SMP_OobDataReply +** +** Description This function is called to provide the OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ + SMP_API extern void SMP_OobDataReply(BD_ADDR bd_addr, tSMP_STATUS res, UINT8 len, + UINT8 *p_data); + +/******************************************************************************* +** +** Function SMP_Encrypt +** +** Description This function is called to encrypt the data with the specified +** key +** +** Parameters: key - Pointer to key key[0] conatins the MSB +** key_len - key length +** plain_text - Pointer to data to be encrypted +** plain_text[0] conatins the MSB +** pt_len - plain text length +** p_out - pointer to the encrypted outputs +** +** Returns Boolean - TRUE: encryption is successful +*******************************************************************************/ + SMP_API extern BOOLEAN SMP_Encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out); + +#ifdef __cplusplus +} +#endif +#endif /* SMP_API_H */ diff --git a/stack/include/uipc_msg.h b/stack/include/uipc_msg.h new file mode 100644 index 0000000..6a9a4ae --- /dev/null +++ b/stack/include/uipc_msg.h @@ -0,0 +1,884 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains sync message over UIPC + * + ******************************************************************************/ + +#ifndef UIPC_MSG_H +#define UIPC_MSG_H + +#include "bt_types.h" + +/****************************************************************************/ +/* UIPC version number: 1.0 */ +/****************************************************************************/ +#define UIPC_VERSION_MAJOR 0x0001 +#define UIPC_VERSION_MINOR 0x0000 + + +/******************************** + + UIPC Management Messages + +********************************/ + +/* tUIPC_STATUS codes*/ +enum +{ + UIPC_STATUS_SUCCESS, + UIPC_STATUS_FAIL +}; +typedef UINT8 tUIPC_STATUS; + +/* op_code */ +#define UIPC_OPEN_REQ 0x00 +#define UIPC_OPEN_RSP 0x01 +#define UIPC_CLOSE_REQ 0x02 +#define UIPC_CLOSE_RSP 0x03 + +/* Structure of UIPC_OPEN_REQ message */ +typedef struct +{ + UINT8 opcode; /* UIPC_OPEN_REQ */ +} tUIPC_OPEN_REQ; +#define UIPC_OPEN_REQ_MSGLEN (1) + +/* Structure of UIPC_OPEN_RSP message */ +typedef struct +{ + UINT8 opcode; /* UIPC_OPEN_RESP */ + tUIPC_STATUS status; /* UIPC_STATUS */ + UINT16 version_major; /* UIPC_VERSION_MAJOR */ + UINT16 version_minor; /* UIPC_VERSION_MINOR */ + UINT8 num_streams; /* Number of simultaneous streams supported by the light stack */ +} tUIPC_OPEN_RSP; +#define UIPC_OPEN_RSP_MSGLEN (7) + +/* Structure of UIPC_CLOSE_REQ message */ +typedef struct t_uipc_close_req +{ + UINT8 opcode; /* UIPC_CLOSE_REQ */ +} tUIPC_CLOSE_REQ; +#define UIPC_CLOSE_REQ_MSGLEN (1) + +/* Structure of UIPC_CLOSE_RSP message, only for BTC, full stack may ignore it */ +typedef struct t_uipc_close_rsp +{ + UINT8 opcode; /* UIPC_CLOSE_RSP */ +} tUIPC_CLOSE_RSP; +#define UIPC_CLOSE_RSP_MSGLEN (1) + +/* UIPC management message structures */ +typedef union +{ + UINT8 opcode; + tUIPC_OPEN_REQ open_req; + tUIPC_OPEN_RSP open_resp; + tUIPC_CLOSE_REQ close_req; +} tUIPC_MSG; + +#define UIPC_MGMT_MSG_MAXLEN (sizeof(tUIPC_MSG)) + +#define IPC_LOG_MSG_LEN 100 +typedef struct t_uipc_log_msg +{ + UINT32 trace_set_mask; + UINT8 msg[IPC_LOG_MSG_LEN]; +} tUIPC_LOG_MSG; +#define UIPC_LOG_MSGLEN (IPC_LOG_MSG_LEN + 4) + +/******************************** + + H5 Sync Message + +********************************/ + +/* op_code */ +#define SLIP_SYNC_TO_LITE_REQ 0 +#define SLIP_SYNC_TO_LITE_RESP 1 +#define SLIP_SYNC_TO_FULL_REQ 2 +#define SLIP_SYNC_TO_FULL_RESP 3 +#define SLIP_SYNC_NOTIFY 4 + +/* status */ +#define SLIP_SYNC_SUCCESS 0 +#define SLIP_SYNC_FAILURE 1 + +typedef struct +{ + UINT8 op_code; + UINT8 status; + UINT16 acl_pkt_size; + UINT8 state; + UINT8 lp_state; /* Low Power state */ + UINT8 next_seqno; /* next send seq */ + UINT8 ack; /* next ack seq, expected seq from peer */ + UINT8 sent_ack; /* last sent ack */ + UINT8 sliding_window_size;/* window size */ + BOOLEAN oof_flow_control; /* Out of Frame SW Flow Control */ + BOOLEAN data_integrity_type;/* Level of Data Integrity Check */ + UINT8 rx_state; /* rx state for incoming packet processing */ +} tSLIP_SYNC_INFO; + +/******************************** + + L2CAP Sync Message + +********************************/ + +/* op_code */ +#define L2C_SYNC_TO_LITE_REQ 0 +#define L2C_SYNC_TO_LITE_RESP 1 +#define L2C_REMOVE_TO_LITE_REQ 2 +#define L2C_REMOVE_TO_LITE_RESP 3 +#define L2C_FLUSH_TO_FULL_IND 4 + +/* status */ +#define L2C_SYNC_SUCCESS 0 +#define L2C_SYNC_FAILURE 1 + +typedef struct t_l2c_stream_info +{ + UINT16 local_cid; /* Local CID */ + UINT16 remote_cid; /* Remote CID */ + UINT16 out_mtu; /* Max MTU we will send */ + UINT16 handle; /* The handle used with LM */ + UINT16 link_xmit_quota; /* Num outstanding pkts allowed */ + BOOLEAN is_flushable; /* TRUE if flushable channel */ +} tL2C_STREAM_INFO; + +typedef struct t_l2c_sync_to_lite_req +{ + UINT8 op_code; /* L2C_SYNC_TO_LITE_REQ */ + UINT16 light_xmit_quota; /* Total quota for light stack */ + UINT16 acl_data_size; /* Max ACL data size across HCI transport */ + UINT16 non_flushable_pbf; /* L2CAP_PKT_START_NON_FLUSHABLE if controller supports */ + /* Otherwise, L2CAP_PKT_START */ + UINT8 multi_av_data_cong_start; /* Multi-AV queue size to start congestion */ + UINT8 multi_av_data_cong_end; /* Multi-AV queue size to end congestion */ + UINT8 multi_av_data_cong_discard; /* Multi-AV queue size to discard */ + UINT8 num_stream; + tL2C_STREAM_INFO stream[BTM_SYNC_INFO_NUM_STR]; +} tL2C_SYNC_TO_LITE_REQ; + +typedef struct t_l2c_sync_to_lite_resp_stream +{ + UINT16 lcid; + UINT8 status; +} tL2C_SYNC_TO_LITE_RESP_STREAM; + +typedef struct t_l2c_sync_to_lite_resp +{ + UINT8 op_code; /* L2C_SYNC_TO_LITE_RESP */ + UINT16 light_xmit_unacked; /* unacked packet more than quota in light stack */ + UINT8 num_stream; + tL2C_SYNC_TO_LITE_RESP_STREAM stream[BTM_SYNC_INFO_NUM_STR]; +} tL2C_SYNC_TO_LITE_RESP; + +typedef struct t_l2c_remove_to_lite_req +{ + UINT8 op_code; /* L2C_REMOVE_TO_LITE_REQ */ + UINT16 light_xmit_quota; /* Total quota for light stack */ + UINT8 num_stream; + UINT16 lcid[BTM_SYNC_INFO_NUM_STR]; +} tL2C_REMOVE_TO_LITE_REQ; + +typedef tL2C_SYNC_TO_LITE_RESP tL2C_REMOVE_TO_LITE_RESP; +typedef tL2C_REMOVE_TO_LITE_REQ tL2C_FLUSH_TO_FULL_IND; + +typedef union t_l2c_sync_msg +{ + UINT8 op_code; + tL2C_SYNC_TO_LITE_REQ sync_req; + tL2C_SYNC_TO_LITE_RESP sync_resp; + tL2C_REMOVE_TO_LITE_REQ remove_req; + tL2C_REMOVE_TO_LITE_RESP remove_resp; + tL2C_FLUSH_TO_FULL_IND flush_ind; +} tL2C_SYNC_MSG; + +/******************************** + + AVDTP Sync Message + +********************************/ + +/* op_code */ +#define AVDT_SYNC_TO_LITE_REQ 0 +#define AVDT_SYNC_TO_LITE_RESP 1 +#define AVDT_RESYNC_TO_LITE_REQ 2 +#define AVDT_RESYNC_TO_LITE_RESP 3 +#define AVDT_SYNC_TO_FULL_REQ 4 +#define AVDT_SYNC_TO_FULL_RESP 5 +#define AVDT_REMOVE_TO_LITE_REQ 6 +#define AVDT_REMOVE_TO_LITE_RESP 7 +#define AVDT_SYNC_TO_BTC_LITE_REQ 8 +#define AVDT_SYNC_TO_BTC_LITE_RESP 9 + +/* status */ +#define AVDT_SYNC_SUCCESS 0 +#define AVDT_SYNC_FAILURE 1 + +typedef struct +{ + UINT16 lcid; + UINT32 ssrc; +} tAVDT_SYNC_TO_BTC_LITE_REQ_STREAM; + +typedef struct +{ + UINT8 opcode; /* AVDT_SYNC_TO_BTC_LITE_REQ */ + UINT8 num_stream; + tAVDT_SYNC_TO_BTC_LITE_REQ_STREAM stream[BTM_SYNC_INFO_NUM_STR]; +} tAVDT_SYNC_TO_BTC_LITE_REQ; + +typedef struct +{ + UINT8 opcode; /* AVDT_SYNC_TO_BTC_LITE_RESP */ + UINT8 status; +} tAVDT_SYNC_TO_BTC_LITE_RESP; + +typedef struct t_avdt_scb_sync_info +{ + UINT8 handle; /* SCB handle */ + BD_ADDR peer_addr; /* BD address of peer */ + UINT16 local_cid; /* Local CID */ + UINT16 peer_mtu; /* L2CAP mtu of the peer device */ + UINT8 mux_tsid_media; /* TSID for media transport session */ + UINT16 media_seq; /* media packet sequence number */ +} tAVDT_SCB_SYNC_INFO; + +typedef struct t_avdt_sync_info +{ + UINT8 op_code; + UINT8 status; + + tAVDT_SCB_SYNC_INFO scb_info[BTM_SYNC_INFO_NUM_STR]; + +} tAVDT_SYNC_INFO; + +typedef union t_avdt_sync_msg +{ + UINT8 op_code; + tAVDT_SYNC_INFO sync_info; + tAVDT_SYNC_TO_BTC_LITE_REQ btc_sync_req; + tAVDT_SYNC_TO_BTC_LITE_RESP btc_sync_resp; +} tAVDT_SYNC_MSG; + +/******************************** + + BTA AV Sync Message + +********************************/ + +/* op_code for MM light stack */ +#define BTA_AV_SYNC_TO_LITE_REQ 0 +#define BTA_AV_SYNC_TO_LITE_RESP 1 +#define BTA_AV_STR_START_TO_LITE_REQ 2 +#define BTA_AV_STR_START_TO_LITE_RESP 3 +#define BTA_AV_STR_STOP_TO_LITE_REQ 4 +#define BTA_AV_STR_STOP_TO_LITE_RESP 5 +#define BTA_AV_STR_CLEANUP_TO_LITE_REQ 6 +#define BTA_AV_STR_CLEANUP_TO_LITE_RESP 7 +#define BTA_AV_STR_SUSPEND_TO_LITE_REQ 8 +#define BTA_AV_STR_SUSPEND_TO_LITE_RESP 9 +#define BTA_AV_SYNC_ERROR_RESP 10 + +/* op_code for BTC light stack */ +#define A2DP_START_REQ 11 +#define A2DP_START_RESP 12 +#define A2DP_STOP_REQ 13 +#define A2DP_STOP_RESP 14 +#define A2DP_CLEANUP_REQ 15 +#define A2DP_CLEANUP_RESP 16 +#define A2DP_SUSPEND_REQ 17 +#define A2DP_SUSPEND_RESP 18 + +#define A2DP_JITTER_DONE_IND 41 /* For BTSNK */ + +#define AUDIO_CODEC_CONFIG_REQ 19 +#define AUDIO_CODEC_CONFIG_RESP 20 +#define AUDIO_CODEC_SET_BITRATE_REQ 21 +#define AUDIO_CODEC_FLUSH_REQ 22 +#define AUDIO_ROUTE_CONFIG_REQ 23 +#define AUDIO_ROUTE_CONFIG_RESP 24 +#define AUDIO_MIX_CONFIG_REQ 25 +#define AUDIO_MIX_CONFIG_RESP 26 +#define AUDIO_BURST_FRAMES_IND 27 +#define AUDIO_BURST_END_IND 28 +#define AUDIO_EQ_MODE_CONFIG_REQ 29 +#define AUDIO_SCALE_CONFIG_REQ 30 + +/* For TIVO, only applicable for I2S -> DAC */ +#define AUDIO_SUB_ROUTE_REQ 51 +#define AUDIO_SUB_ROUTE_RESP 52 + +typedef struct +{ + UINT8 opcode; /* A2DP_START_REQ */ + UINT16 lcid; + UINT16 curr_mtu; +}tA2DP_START_REQ; + +typedef struct +{ + UINT8 opcode; /* A2DP_STOP_REQ */ + UINT16 lcid; +}tA2DP_STOP_REQ; + +typedef struct +{ + UINT8 opcode; /* A2DP_SUSPEND_REQ */ + UINT16 lcid; +}tA2DP_SUSPEND_REQ; + +typedef struct +{ + UINT8 opcode; /* A2DP_CLEANUP_REQ */ + UINT16 lcid; + UINT16 curr_mtu; +} tA2DP_CLEANUP_REQ; + +typedef struct +{ + UINT8 opcode; /* A2DP_START_RESP, A2DP_STOP_RESP, A2DP_CLEANUP_RESP, A2DP_SUSPEND_RESP */ + UINT16 lcid; +}tA2DP_GENERIC_RESP; + +#define AUDIO_CODEC_NONE 0x0000 +#define AUDIO_CODEC_SBC_ENC 0x0001 +#define AUDIO_CODEC_SBC_DEC 0x0002 +#define AUDIO_CODEC_MP3_ENC 0x0004 +#define AUDIO_CODEC_MP3_DEC 0x0008 +#define AUDIO_CODEC_AAC_ENC 0x0010 +#define AUDIO_CODEC_AAC_DEC 0x0020 +#define AUDIO_CODEC_AAC_PLUS_ENC 0x0040 +#define AUDIO_CODEC_AAC_PLUS_DEC 0x0080 +#define AUDIO_CODEC_MP2_ENC 0x0100 +#define AUDIO_CODEC_MP2_DEC 0x0200 +#define AUDIO_CODEC_MP2_5_ENC 0x0400 +#define AUDIO_CODEC_MP2_5_DEC 0x0800 + +typedef UINT16 tAUDIO_CODEC_TYPE; + +/* SBC CODEC Parameters */ + +#define CODEC_INFO_SBC_SF_16K 0x00 +#define CODEC_INFO_SBC_SF_32K 0x01 +#define CODEC_INFO_SBC_SF_44K 0x02 +#define CODEC_INFO_SBC_SF_48K 0x03 + +#define CODEC_INFO_SBC_BLOCK_4 0x00 +#define CODEC_INFO_SBC_BLOCK_8 0x01 +#define CODEC_INFO_SBC_BLOCK_12 0x02 +#define CODEC_INFO_SBC_BLOCK_16 0x03 + +#define CODEC_INFO_SBC_CH_MONO 0x00 +#define CODEC_INFO_SBC_CH_DUAL 0x01 +#define CODEC_INFO_SBC_CH_STEREO 0x02 +#define CODEC_INFO_SBC_CH_JS 0x03 + +#define CODEC_INFO_SBC_ALLOC_LOUDNESS 0x00 +#define CODEC_INFO_SBC_ALLOC_SNR 0x01 + +#define CODEC_INFO_SBC_SUBBAND_4 0x00 +#define CODEC_INFO_SBC_SUBBAND_8 0x01 + +/* MPEG audio version ID */ +#define CODEC_INFO_MP25_ID 0x00 +#define CODEC_INFO_RESERVE 0x01 +#define CODEC_INFO_MP2_ID 0x02 +#define CODEC_INFO_MP3_ID 0x03 + +#define CODEC_INFO_MP3_PROTECTION_ON 0x00 +#define CODEC_INFO_MP3_PROTECTION_OFF 0x01 + +#define CODEC_INFO_MP3_BR_IDX_FREE 0x00 +#define CODEC_INFO_MP3_BR_IDX_32K 0x01 +#define CODEC_INFO_MP3_BR_IDX_40K 0x02 +#define CODEC_INFO_MP3_BR_IDX_48K 0x03 +#define CODEC_INFO_MP3_BR_IDX_56K 0x04 +#define CODEC_INFO_MP3_BR_IDX_64K 0x05 +#define CODEC_INFO_MP3_BR_IDX_80K 0x06 +#define CODEC_INFO_MP3_BR_IDX_96K 0x07 +#define CODEC_INFO_MP3_BR_IDX_112K 0x08 +#define CODEC_INFO_MP3_BR_IDX_128K 0x09 +#define CODEC_INFO_MP3_BR_IDX_160K 0x0A +#define CODEC_INFO_MP3_BR_IDX_192K 0x0B +#define CODEC_INFO_MP3_BR_IDX_224K 0x0C +#define CODEC_INFO_MP3_BR_IDX_256K 0x0D +#define CODEC_INFO_MP3_BR_IDX_320K 0x0E + +#define CODEC_INFO_MP3_SF_44K 0x00 +#define CODEC_INFO_MP3_SF_48K 0x01 +#define CODEC_INFO_MP3_SF_32K 0x02 + +#define CODEC_INFO_MP3_MODE_STEREO 0x00 +#define CODEC_INFO_MP3_MODE_JS 0x01 +#define CODEC_INFO_MP3_MODE_DUAL 0x02 +#define CODEC_INFO_MP3_MODE_SINGLE 0x03 + +/* layer 3, type of joint stereo coding method (intensity and ms) */ +#define CODEC_INFO_MP3_MODE_EXT_OFF_OFF 0x00 +#define CODEC_INFO_MP3_MODE_EXT_ON_OFF 0x01 +#define CODEC_INFO_MP3_MODE_EXT_OFF_ON 0x02 +#define CODEC_INFO_MP3_MODE_EXT_ON_ON 0x03 + + +#define CODEC_INFO_MP2_PROTECTION_ON 0x00 +#define CODEC_INFO_MP2_PROTECTION_OFF 0x01 + +#define CODEC_INFO_MP2_BR_IDX_FREE 0x00 +#define CODEC_INFO_MP2_BR_IDX_8K 0x01 +#define CODEC_INFO_MP2_BR_IDX_16K 0x02 +#define CODEC_INFO_MP2_BR_IDX_24K 0x03 +#define CODEC_INFO_MP2_BR_IDX_32K 0x04 +#define CODEC_INFO_MP2_BR_IDX_40K 0x05 +#define CODEC_INFO_MP2_BR_IDX_48K 0x06 +#define CODEC_INFO_MP2_BR_IDX_56K 0x07 +#define CODEC_INFO_MP2_BR_IDX_64K 0x08 +#define CODEC_INFO_MP2_BR_IDX_80K 0x09 +#define CODEC_INFO_MP2_BR_IDX_96K 0x0A +#define CODEC_INFO_MP2_BR_IDX_112K 0x0B +#define CODEC_INFO_MP2_BR_IDX_128K 0x0C +#define CODEC_INFO_MP2_BR_IDX_144K 0x0D +#define CODEC_INFO_MP2_BR_IDX_160K 0x0E + +#define CODEC_INFO_MP2_SF_22K 0x00 +#define CODEC_INFO_MP2_SF_24K 0x01 +#define CODEC_INFO_MP2_SF_16K 0x02 + +#define CODEC_INFO_MP2_MODE_STEREO 0x00 +#define CODEC_INFO_MP2_MODE_JS 0x01 +#define CODEC_INFO_MP2_MODE_DUAL 0x02 +#define CODEC_INFO_MP2_MODE_SINGLE 0x03 + +/* layer 3, type of joint stereo coding method (intensity and ms) */ +#define CODEC_INFO_MP2_MODE_EXT_OFF_OFF 0x00 +#define CODEC_INFO_MP2_MODE_EXT_ON_OFF 0x01 +#define CODEC_INFO_MP2_MODE_EXT_OFF_ON 0x02 +#define CODEC_INFO_MP2_MODE_EXT_ON_ON 0x03 + +#define CODEC_INFO_MP2_SAMPLE_PER_FRAME 576 + +/* mpeg 2.5 layer 3 decoder */ + +#define CODEC_INFO_MP25_PROTECTION_ON 0x00 +#define CODEC_INFO_MP25_PROTECTION_OFF 0x01 + +#define CODEC_INFO_MP25_BR_IDX_FREE 0x00 +#define CODEC_INFO_MP25_BR_IDX_8K 0x01 +#define CODEC_INFO_MP25_BR_IDX_16K 0x02 +#define CODEC_INFO_MP25_BR_IDX_24K 0x03 +#define CODEC_INFO_MP25_BR_IDX_32K 0x04 +#define CODEC_INFO_MP25_BR_IDX_40K 0x05 +#define CODEC_INFO_MP25_BR_IDX_48K 0x06 +#define CODEC_INFO_MP25_BR_IDX_56K 0x07 +#define CODEC_INFO_MP25_BR_IDX_64K 0x08 +#define CODEC_INFO_MP25_BR_IDX_80K 0x09 +#define CODEC_INFO_MP25_BR_IDX_96K 0x0A +#define CODEC_INFO_MP25_BR_IDX_112K 0x0B +#define CODEC_INFO_MP25_BR_IDX_128K 0x0C +#define CODEC_INFO_MP25_BR_IDX_144K 0x0D +#define CODEC_INFO_MP25_BR_IDX_160K 0x0E + +#define CODEC_INFO_MP25_SF_11K 0x00 +#define CODEC_INFO_MP25_SF_12K 0x01 +#define CODEC_INFO_MP25_SF_8K 0x02 + +#define CODEC_INFO_MP25_MODE_STEREO 0x00 +#define CODEC_INFO_MP25_MODE_JS 0x01 +#define CODEC_INFO_MP25_MODE_DUAL 0x02 +#define CODEC_INFO_MP25_MODE_SINGLE 0x03 + +/* layer 3, type of joint stereo coding method (intensity and ms) */ +#define CODEC_INFO_MP25_MODE_EXT_OFF_OFF 0x00 +#define CODEC_INFO_MP25_MODE_EXT_ON_OFF 0x01 +#define CODEC_INFO_MP25_MODE_EXT_OFF_ON 0x02 +#define CODEC_INFO_MP25_MODE_EXT_ON_ON 0x03 + +#define CODEC_INFO_MP25_SAMPLE_PER_FRAME 576 + +/* AAC/AAC+ CODEC Parameters */ +#define CODEC_INFO_AAC_SF_IDX_96K 0x0 +#define CODEC_INFO_AAC_SF_IDX_88K 0x1 +#define CODEC_INFO_AAC_SF_IDX_64K 0x2 +#define CODEC_INFO_AAC_SF_IDX_48K 0x3 +#define CODEC_INFO_AAC_SF_IDX_44K 0x4 +#define CODEC_INFO_AAC_SF_IDX_32K 0x5 +#define CODEC_INFO_AAC_SF_IDX_24K 0x6 +#define CODEC_INFO_AAC_SF_IDX_22K 0x7 +#define CODEC_INFO_AAC_SF_IDX_16K 0x8 +#define CODEC_INFO_AAC_SF_IDX_12K 0x9 +#define CODEC_INFO_AAC_SF_IDX_11K 0xA +#define CODEC_INFO_AAC_SF_IDX_08K 0xB +#define CODEC_INFO_AAC_SF_IDX_RESERVE 0xC + +#define CODEC_INFO_AAC_BR_RATE_48K 288000 +#define CODEC_INFO_AAC_BR_RATE_44K 264600 +#define CODEC_INFO_AAC_BR_RATE_32K 192000 + + +#define CODEC_INFO_AAC_1_CH 1 /*center front speaker */ +#define CODEC_INFO_AAC_2_CH 2 /*left, right front speaker */ +#define CODEC_INFO_AAC_3_CH 3 /*center front speaker, left right front speaker */ +#define CODEC_INFO_AAC_4_CH 4 /*center/rear front speaker, left/right front speaker */ +#define CODEC_INFO_AAC_5_CH 5 /*center, left, right front speaker, left/right surround */ +#define CODEC_INFO_AAC_6_CH 6 /*center, left, right front speaker, left/right surround, LFE */ +#define CODEC_INFO_AAC_7_CH 7 /*(left, right)center/left,right front speaker, left/right surround, LFE */ + + +typedef struct +{ + UINT8 sampling_freq; + UINT8 channel_mode; + UINT8 block_length; + UINT8 num_subbands; + UINT8 alloc_method; + UINT8 bitpool_size; /* 2 - 250 */ +} tCODEC_INFO_SBC; + +typedef struct +{ + UINT8 ch_mode; + UINT8 sampling_freq; + UINT8 bitrate_index; /* 0 - 14 */ +} tCODEC_INFO_MP3; + +typedef struct +{ + UINT8 ch_mode; + UINT8 sampling_freq; + UINT8 bitrate_index; /* 0 - 14 */ +} tCODEC_INFO_MP2; + + +typedef struct +{ + UINT8 ch_mode; + UINT8 sampling_freq; + UINT8 bitrate_index; /* 0 - 14 */ +} tCODEC_INFO_MP2_5; + +typedef struct +{ + UINT16 sampling_freq; + UINT8 channel_mode; /* 0x02:mono, 0x01:dual */ + UINT32 bitrate; /* 0 - 320K */ + UINT32 sbr_profile; /* 1: ON, 0: OFF */ +} tCODEC_INFO_AAC; + +typedef union +{ + tCODEC_INFO_SBC sbc; + tCODEC_INFO_MP3 mp3; + tCODEC_INFO_MP2 mp2; + tCODEC_INFO_MP2_5 mp2_5; + tCODEC_INFO_AAC aac; +} tCODEC_INFO; + +typedef struct +{ + UINT8 opcode; /* AUDIO_CODEC_CONFIG_REQ */ + tAUDIO_CODEC_TYPE codec_type; + tCODEC_INFO codec_info; +} tAUDIO_CODEC_CONFIG_REQ; + +#define AUDIO_CONFIG_SUCCESS 0x00 +#define AUDIO_CONFIG_NOT_SUPPORTED 0x01 +#define AUDIO_CONFIG_FAIL_OUT_OF_MEMORY 0x02 +#define AUDIO_CONFIG_FAIL_CODEC_USED 0x03 +#define AUDIO_CONFIG_FAIL_ROUTE 0x04 +typedef UINT8 tAUDIO_CONFIG_STATUS; + +typedef struct +{ + UINT8 opcode; /* AUDIO_CODEC_CONFIG_RESP */ + tAUDIO_CONFIG_STATUS status; +} tAUDIO_CODEC_CONFIG_RESP; + +typedef struct +{ + UINT8 opcode; /* AUDIO_CODEC_SET_BITRATE_REQ */ + tAUDIO_CODEC_TYPE codec_type; + union + { + UINT8 sbc; + UINT8 mp3; + UINT32 aac; + } codec_bitrate; +} tAUDIO_CODEC_SET_BITRATE_REQ; + +#define AUDIO_ROUTE_SRC_FMRX 0x00 +#define AUDIO_ROUTE_SRC_I2S 0x01 +#define AUDIO_ROUTE_SRC_ADC 0x02 +#define AUDIO_ROUTE_SRC_HOST 0x03 +#define AUDIO_ROUTE_SRC_PTU 0x04 +#define AUDIO_ROUTE_SRC_BTSNK 0x05 +#define AUDIO_ROUTE_SRC_NONE 0x80 +#define MAX_AUDIO_ROUTE_SRC 6 +typedef UINT8 tAUDIO_ROUTE_SRC; + +#define AUDIO_ROUTE_MIX_NONE 0x00 +#define AUDIO_ROUTE_MIX_HOST 0x01 +#define AUDIO_ROUTE_MIX_PCM 0x02 +#define AUDIO_ROUTE_MIX_CHIRP 0x03 +#define AUDIO_ROUTE_MIX_I2S 0x04 +#define AUDIO_ROUTE_MIX_ADC 0x05 +#define AUDIO_ROUTE_MIX_RESERVED 0x06 +#define MAX_AUDIO_ROUTE_MIX 7 +typedef UINT8 tAUDIO_ROUTE_MIX; + +#define AUDIO_ROUTE_OUT_NONE 0x0000 +#define AUDIO_ROUTE_OUT_BTA2DP 0x0001 +#define AUDIO_ROUTE_OUT_FMTX 0x0002 +#define AUDIO_ROUTE_OUT_BTSCO 0x0004 +#define AUDIO_ROUTE_OUT_HOST 0x0008 +#define AUDIO_ROUTE_OUT_DAC 0x0010 +#define AUDIO_ROUTE_OUT_I2S 0x0020 +#define AUDIO_ROUTE_OUT_BTA2DP_DAC 0x0040 +#define AUDIO_ROUTE_OUT_BTA2DP_I2S 0x0080 +#define AUDIO_ROUTE_OUT_BTSCO_DAC 0x0100 +#define AUDIO_ROUTE_OUT_BTSCO_I2S 0x0200 +#define AUDIO_ROUTE_OUT_HOST_BTA2DP 0x0400 +#define AUDIO_ROUTE_OUT_HOST_BTSCO 0x0800 +#define AUDIO_ROUTE_OUT_HOST_DAC 0x1000 +#define AUDIO_ROUTE_OUT_HOST_I2S 0x2000 +#define AUDIO_ROUTE_OUT_DAC_I2S 0x4000 +#define AUDIO_ROUTE_OUT_RESERVED_2 0x8000 + +#define MAX_AUDIO_SINGLE_ROUTE_OUT 6 +#define MAX_AUDIO_MULTI_ROUTE_OUT 16 +typedef UINT16 tAUDIO_MULTI_ROUTE_OUT; +typedef UINT8 tAUDIO_ROUTE_OUT; + +#define AUDIO_ROUTE_SF_8K 0x00 +#define AUDIO_ROUTE_SF_16K 0x01 +#define AUDIO_ROUTE_SF_32K 0x02 +#define AUDIO_ROUTE_SF_44_1K 0x03 +#define AUDIO_ROUTE_SF_48K 0x04 +#define AUDIO_ROUTE_SF_11K 0x05 +#define AUDIO_ROUTE_SF_12K 0x06 +#define AUDIO_ROUTE_SF_22K 0x07 +#define AUDIO_ROUTE_SF_24K 0x08 +#define AUDIO_ROUTE_SF_NA 0xFF +typedef UINT8 tAUDIO_ROUTE_SF; + +#define AUDIO_ROUTE_EQ_BASS_BOOST 0x00 +#define AUDIO_ROUTE_EQ_CLASSIC 0x01 +#define AUDIO_ROUTE_EQ_JAZZ 0x02 +#define AUDIO_ROUTE_EQ_LIVE 0x03 +#define AUDIO_ROUTE_EQ_NORMAL 0x04 +#define AUDIO_ROUTE_EQ_ROCK 0x05 +#define AUDIO_ROUTE_EQ_BYPASS 0x06 + +#define AUDIO_ROUTE_DIGITAL_VOLUME_CONTROL 0x07 + +#define AUDIO_ROUTE_EQ_CONFIG_GAIN 0xFF /* Custion Gain Config */ +typedef UINT8 tAUDIO_ROUTE_EQ; + +typedef struct +{ + UINT8 opcode; /* AUDIO_ROUTE_CONFIG_REQ */ + tAUDIO_ROUTE_SRC src; + tAUDIO_ROUTE_SF src_sf; + tAUDIO_ROUTE_OUT out; + tAUDIO_ROUTE_SF out_codec_sf; + tAUDIO_ROUTE_SF out_i2s_sf; + tAUDIO_ROUTE_EQ eq_mode; +} tAUDIO_ROUTE_CONFIG_REQ; + +typedef struct +{ + UINT8 opcode; /* AUDIO_ROUTE_CONFIG_RESP */ + tAUDIO_CONFIG_STATUS status; +} tAUDIO_ROUTE_CONFIG_RESP; + +typedef struct +{ + UINT16 amp[2]; /* left/right 15 bit amplitude value */ + UINT16 tone[2]; /* left/right 12 bit frequency 0 - 4096Hz */ + UINT16 mark[2]; /* left/right 16 bit mark time 0 - 65535ms */ + UINT16 space[2]; /* left/right 16 bit space time 0 - 65535ms */ +} tCHIRP_CONFIG; + +typedef struct +{ + UINT8 pri_l; /* Primary Left scale : 0 ~ 255 */ + UINT8 mix_l; /* Mixing Left scale : 0 ~ 255 */ + UINT8 pri_r; /* Primary Right scale : 0 ~ 255 */ + UINT8 mix_r; /* Mixing Right scale : 0 ~ 255 */ +} tMIX_SCALE_CONFIG; + +/* For custon equalizer gain configuration */ +typedef struct +{ + UINT32 audio_l_g0; /* IIR biquad filter left ch gain 0 */ + UINT32 audio_l_g1; /* IIR biquad filter left ch gain 1 */ + UINT32 audio_l_g2; /* IIR biquad filter left ch gain 2 */ + UINT32 audio_l_g3; /* IIR biquad filter left ch gain 3 */ + UINT32 audio_l_g4; /* IIR biquad filter left ch gain 4 */ + UINT32 audio_l_gl; /* IIR biquad filter left ch global gain */ + UINT32 audio_r_g0; /* IIR biquad filter left ch gain 0 */ + UINT32 audio_r_g1; /* IIR biquad filter left ch gain 1 */ + UINT32 audio_r_g2; /* IIR biquad filter left ch gain 2 */ + UINT32 audio_r_g3; /* IIR biquad filter left ch gain 3 */ + UINT32 audio_r_g4; /* IIR biquad filter left ch gain 4 */ + UINT32 audio_r_gl; /* IIR biquad filter left ch global gain */ +} tEQ_GAIN_CONFIG; + +typedef struct +{ + UINT8 opcode; /* AUDIO_MIX_CONFIG_REQ */ + tAUDIO_ROUTE_MIX mix_src; + tAUDIO_ROUTE_SF mix_src_sf; + tMIX_SCALE_CONFIG mix_scale; + tCHIRP_CONFIG chirp_config; +} tAUDIO_MIX_CONFIG_REQ; + +typedef struct +{ + UINT8 opcode; /* AUDIO_MIX_CONFIG_RESP */ + tAUDIO_CONFIG_STATUS status; +} tAUDIO_MIX_CONFIG_RESP; + + +typedef struct +{ + UINT8 opcode; /* AUDIO_BURST_FRAMES_IND */ + UINT32 burst_size; /* in bytes */ +} tAUDIO_BURST_FRAMES_IND; + +typedef struct +{ + UINT8 opcode; /* AUDIO_BURST_END_IND */ +} tAUDIO_BURST_END_IND; + +typedef struct +{ + UINT8 opcode; /* AUDIO_CODEC_FLUSH_REQ */ +} tAUDIO_CODEC_FLUSH_REQ; + +typedef struct +{ + UINT8 opcode; /* AUDIO_EQ_MODE_CONFIG_REQ */ + tAUDIO_ROUTE_EQ eq_mode; + tEQ_GAIN_CONFIG filter_gain; /* Valid only when eq_mode is 0xFF */ +} tAUDIO_EQ_MODE_CONFIG_REQ; + +typedef struct +{ + UINT8 opcode; /* AUDIO_SCALE_CONFIG_REQ */ + tMIX_SCALE_CONFIG mix_scale; +} tAUDIO_SCALE_CONFIG_REQ; + +typedef UINT8 tBTA_AV_DUAL_STACK_EVT; + +typedef struct +{ + UINT8 avdt_handle; /* AVDTP handle */ + UINT8 chnl; /* the channel: audio/video */ + UINT8 codec_type; /* codec type */ + BOOLEAN cong; /* TRUE if AVDTP congested */ + UINT8 hdi; /* the index to SCB[] */ + UINT8 hndl; /* the handle: ((hdi + 1)|chnl) */ + UINT8 l2c_bufs; /* the number of buffers queued to L2CAP */ + UINT16 l2c_cid; /* L2CAP channel ID */ + BD_ADDR peer_addr; /* peer BD address */ +}tBTA_AV_SYNC_INFO; + +typedef struct +{ + tBTA_AV_DUAL_STACK_EVT event; + tBTA_AV_SYNC_INFO sync_info; + UINT16 curr_mtu; /* common mtu shared by all active streams */ + UINT8 multi_av_supported; /* Whether multi-av is supported */ +}tBTA_AV_SYNC_INFO_REQ; /* SYNC_TO_LITE_REQ */ + +/* Dual stack stream events */ +typedef struct +{ + tBTA_AV_DUAL_STACK_EVT event; + UINT8 scb_idx; +}tBTA_AV_SCB_EVT; + +/* data type for the Audio Codec Information*/ +typedef struct +{ + UINT16 bit_rate; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_busy; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_swampd; /* SBC encoder bit rate in kbps */ + UINT8 busy_level; /* Busy level indicating the bit-rate to be used */ + UINT8 codec_info[AVDT_CODEC_SIZE]; + UINT8 codec_type; /* Codec type */ +} tBTA_AV_AUDIO_CODEC_SYNC_INFO; + +/* Dual stack stream events */ +typedef struct +{ + tBTA_AV_DUAL_STACK_EVT event; + UINT8 scb_idx; + UINT8 audio_open_cnt; + tBTA_AV_AUDIO_CODEC_SYNC_INFO p_codec_cfg; + UINT8 start_stop_flag; +}tBTA_AV_SCB_REQ; + +typedef struct +{ + tBTA_AV_DUAL_STACK_EVT event; + UINT8 scb_idx; + UINT8 audio_open_cnt; + UINT16 curr_mtu; /* common mtu shared by all active streams */ +}tBTA_AV_SCB_CLEANUP_REQ; + +/* Add request/response structures if needed ... +typedef struct +{ + event; + data; +}tBTA_AV_SYNC_*_REQ/RESP; +*/ + +typedef union +{ + /* MM light stack */ + tBTA_AV_DUAL_STACK_EVT event; + tBTA_AV_SYNC_INFO_REQ sync_info_req; + tBTA_AV_SCB_EVT scb_evt; + tBTA_AV_SCB_REQ scb_req; + tBTA_AV_SCB_CLEANUP_REQ scb_cleanup_req; + + /* BTC light stack */ + UINT8 opcode; + tA2DP_START_REQ btc_start_req; + tA2DP_STOP_REQ btc_stop_req; + tA2DP_CLEANUP_REQ btc_cleanup_req; + tA2DP_SUSPEND_REQ btc_suspend_req; + + tAUDIO_CODEC_CONFIG_REQ codec_config_req; + tAUDIO_CODEC_SET_BITRATE_REQ codec_bitrate_req; + tAUDIO_CODEC_FLUSH_REQ codec_flush_req; + tAUDIO_ROUTE_CONFIG_REQ route_config_req; + tAUDIO_MIX_CONFIG_REQ mix_config_req; + tAUDIO_EQ_MODE_CONFIG_REQ eq_mode_req; + tAUDIO_SCALE_CONFIG_REQ scale_config_req; +}tBTA_DUAL_STACK_MSG; + +#endif /* UIPC_MSG_H */ diff --git a/stack/include/utfc.h b/stack/include/utfc.h new file mode 100644 index 0000000..787efe8 --- /dev/null +++ b/stack/include/utfc.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * UTF conversion utilities. + * + ******************************************************************************/ +#ifndef UTFC_H +#define UTFC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/******************************************************************************* +** +** Function utfc_16_to_8 +** +** Description Convert a UTF-16 array to a null-terminated UTF-8 string. +** Illegal characters are skipped. +** +** Returns Length of UTF-8 string in bytes. +** +*******************************************************************************/ +extern UINT16 utfc_16_to_8(UINT8 *p_utf8, UINT16 utf8_len, UINT16 *p_utf16, UINT16 utf16_len); + +/******************************************************************************* +** +** Function utfc_8_to_16 +** +** Description Convert a null-terminated UTF-8 string to a UTF-16 array. +** Illegal characters are skipped. The UTF-16 array is +** appended with a zero (null) character. +** +** Returns Length of UTF-16 array including null character. +** +*******************************************************************************/ +extern UINT16 utfc_8_to_16(UINT16 *p_utf16, UINT16 utf16_len, UINT8 *p_utf8); + +#ifdef __cplusplus +} +#endif + +#endif /* UTFC_H */ diff --git a/stack/include/wbt_api.h b/stack/include/wbt_api.h new file mode 100644 index 0000000..8bfb772 --- /dev/null +++ b/stack/include/wbt_api.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * + * 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 file contains definitions and constants used by the Broadcom + * Bluetooth Extensions API software. + * + ******************************************************************************/ +#ifndef WBT_API_H +#define WBT_API_H + +#include "bt_target.h" + +/***************************************************************************** +** Constants and Types +*****************************************************************************/ + +/************************** +* SDP Attribute IDs * +***************************/ +#define ATTR_ID_EXT_BRCM_VERSION 0x8001 /* UINT16 (0xmmnn - major, minor [0x0001]) mandatory */ +#define ATTR_ID_EXT_PIN_CODE 0x8002 /* UINT32 4 - digit pin */ + +/************************** +* SDP Attribute ID Values * +***************************/ +/* Version Attribute Value */ +#define BRCM_EXT_VERSION 0x0001 /* UINT16 (0xmmnn - major, minor [0x0001]) mandatory */ + +/* Pin Code Attribute Value */ +#define BRCM_EXT_PIN_CODE 0x00000000 /* UINT32 ('0000') */ + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +BT_API extern BOOLEAN WBT_ExtCreateRecord(void); + +/*** Features ***/ +BT_API extern BOOLEAN WBT_ExtAddPinCode(void); + + +BT_API extern UINT32 wbt_sdp_show_ext(UINT8 scn, char *service_name, + UINT8 pin_code_ext, + UINT8 active_sync_ext); + +#ifdef __cplusplus +} +#endif + +#endif /* WBT_API_H */ diff --git a/stack/include/wcassert.h b/stack/include/wcassert.h new file mode 100644 index 0000000..a1d7b47 --- /dev/null +++ b/stack/include/wcassert.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef WC_ASSERT_H +#define WC_ASSERT_H + + +#ifdef _DEBUG + +#include "bt_target.h" + + + +/* debug settings*/ +#ifndef WC_DEBUG_LEVEL +#define WC_DEBUG_LEVEL 0 +#endif + +#if WC_DEBUG_LEVEL == 0 + +#include "stdio.h" /* for printf()*/ + +#ifdef __cplusplus +extern "C" wc_assert(char *message, char *file, UINT32 line); +#else +void wc_assert(char *message, char *file, UINT32 line); +#endif + +#define WC_ASSERT(_x) if ( !(_x) ) wc_assert("ASSERT at %s line %d\n", __FILE__, __LINE__); +#define WC_ASSERT_ALWAYS() wc_assert("ASSERT! at %s line %d\n", __FILE__, __LINE__); + +#elif WC_DEBUG_LEVEL == 1 + +#include "assert.h" + +#define WC_ASSERT(_x) assert(_x); +#define WC_ASSERT_ALWAYS() assert(0); +#endif /* WC_DEBUG_LEVEL*/ + +#else /* _DEBUG*/ + +#ifndef WC_ASSERT +#define WC_ASSERT(_x) ; +#endif + +#ifndef WC_ASSERT_ALWAYS +#define WC_ASSERT_ALWAYS() ; +#endif + +#endif /* _DEBUG*/ +#endif /* WC_ASSERT_H*/ diff --git a/stack/l2cap/l2c_api.c b/stack/l2cap/l2c_api.c new file mode 100644 index 0000000..691d386 --- /dev/null +++ b/stack/l2cap/l2c_api.c @@ -0,0 +1,1812 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP API code + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "btu.h" +#include "btm_api.h" + +/******************************************************************************* +** +** Function L2CA_Register +** +** Description Other layers call this function to register for L2CAP +** services. +** +** Returns PSM to use or zero if error. Typically, the PSM returned +** is the same as was passed in, but for an outgoing-only +** connection to a dynamic PSM, a "virtual" PSM is returned +** and should be used in the calls to L2CA_ConnectReq(), +** L2CA_ErtmConnectReq() and L2CA_Deregister() +** +*******************************************************************************/ +UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info) +{ + tL2C_RCB *p_rcb; + UINT16 vpsm = psm; + + L2CAP_TRACE_API1 ("L2CAP - L2CA_Register() called for PSM: 0x%04x", psm); + + /* Verify that the required callback info has been filled in + ** Note: Connection callbacks are required but not checked + ** for here because it is possible to be only a client + ** or only a server. + */ + if ((!p_cb_info->pL2CA_ConfigCfm_Cb) + || (!p_cb_info->pL2CA_ConfigInd_Cb) + || (!p_cb_info->pL2CA_DataInd_Cb) + || (!p_cb_info->pL2CA_DisconnectInd_Cb)) + { + L2CAP_TRACE_ERROR1 ("L2CAP - no cb registering PSM: 0x%04x", psm); + return (0); + } + + /* Verify PSM is valid */ + if (L2C_INVALID_PSM(psm)) + { + L2CAP_TRACE_ERROR1 ("L2CAP - invalid PSM value, PSM: 0x%04x", psm); + return (0); + } + + /* Check if this is a registration for an outgoing-only connection to */ + /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */ + if ( (psm >= 0x1001) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL) ) + { + for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2) + { + if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) + break; + } + + L2CAP_TRACE_API2 ("L2CA_Register - Real PSM: 0x%04x Virtual PSM: 0x%04x", psm, vpsm); + } + + /* If registration block already there, just overwrite it */ + if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) + { + if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL) + { + L2CAP_TRACE_WARNING2 ("L2CAP - no RCB available, PSM: 0x%04x vPSM: 0x%04x", psm, vpsm); + return (0); + } + } + + p_rcb->api = *p_cb_info; + p_rcb->real_psm = psm; + + return (vpsm); +} + + + +/******************************************************************************* +** +** Function L2CA_Deregister +** +** Description Other layers call this function to de-register for L2CAP +** services. +** +** Returns void +** +*******************************************************************************/ +void L2CA_Deregister (UINT16 psm) +{ + tL2C_RCB *p_rcb; + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + int ii; + + L2CAP_TRACE_API1 ("L2CAP - L2CA_Deregister() called for PSM: 0x%04x", psm); + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) != NULL) + { + p_lcb = &l2cb.lcb_pool[0]; + for (ii = 0; ii < MAX_L2CAP_LINKS; ii++, p_lcb++) + { + if (p_lcb->in_use) + { + if (((p_ccb = p_lcb->ccb_queue.p_first_ccb) == NULL) + || (p_lcb->link_state == LST_DISCONNECTING)) + continue; + + if ((p_ccb->in_use) && + ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) || + (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) + continue; + + if (p_ccb->p_rcb == p_rcb) + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + } + } + l2cu_release_rcb (p_rcb); + } + else + { + L2CAP_TRACE_WARNING1 ("L2CAP - PSM: 0x%04x not found for deregistration", psm); + } +} + +/******************************************************************************* +** +** Function L2CA_AllocatePSM +** +** Description Other layers call this function to find an unused PSM for L2CAP +** services. +** +** Returns PSM to use. +** +*******************************************************************************/ +UINT16 L2CA_AllocatePSM(void) +{ + BOOLEAN done = FALSE; + UINT16 psm = l2cb.dyn_psm; + + L2CAP_TRACE_API0( "L2CA_AllocatePSM"); + while (!done) + { + psm += 2; + if (psm > 0xfeff) + { + psm = 0x1001; + } + else if (psm & 0x0100) + { + /* the upper byte must be even */ + psm += 0x0100; + } + + /* if psm is in range of reserved BRCM Aware features */ + if ((BRCM_RESERVED_PSM_START <= psm)&&(psm <= BRCM_RESERVED_PSM_END)) + continue; + + /* make sure the newlly allocated psm is not used right now */ + if ((l2cu_find_rcb_by_psm (psm)) == NULL) + done = TRUE; + } + l2cb.dyn_psm = psm; + + return(psm); +} + +/******************************************************************************* +** +** Function L2CA_ConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr) +{ + return L2CA_ErtmConnectReq (psm, p_bd_addr, NULL); +} + +/******************************************************************************* +** +** Function L2CA_ErtmConnectReq +** +** Description Higher layers call this function to create an L2CAP connection. +** Note that the connection is not established at this time, but +** connection establishment gets started. The callback function +** will be invoked when connection establishes or fails. +** +** Returns the CID of the connection, or 0 if it failed to start +** +*******************************************************************************/ +UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_ertm_info) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API6 ("L2CA_ErtmConnectReq() PSM: 0x%04x BDA: %08x%04x p_ertm_info: 0x%08x allowed:0x%x preferred:%d", psm, + (p_bd_addr[0]<<24)+(p_bd_addr[1]<<16)+(p_bd_addr[2]<<8)+p_bd_addr[3], + (p_bd_addr[4]<<8)+p_bd_addr[5], p_ertm_info, + (p_ertm_info) ? p_ertm_info->allowed_modes : 0, (p_ertm_info) ? p_ertm_info->preferred_mode : 0); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING0 ("L2CAP connect req - BTU not ready"); + return (0); + } + /* Fail if the PSM is not registered */ + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm); + return (0); + } + + /* First, see if we already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL) + { + /* No link. Get an LCB and start link establishment */ + if ( ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL) + || (l2cu_create_conn(p_lcb) == FALSE) ) + { + L2CAP_TRACE_WARNING2 ("L2CAP - conn not started for PSM: 0x%04x p_lcb: 0x%08x", psm, p_lcb); + return (0); + } + } + + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm); + return (0); + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + if (p_ertm_info) + { + p_ccb->ertm_info = *p_ertm_info; + + /* Replace default indicators with the actual default pool */ + if (p_ccb->ertm_info.fcr_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID; + + if (p_ccb->ertm_info.fcr_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID; + + if (p_ccb->ertm_info.user_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID; + + if (p_ccb->ertm_info.user_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID; + + p_ccb->max_rx_mtu = GKI_get_pool_bufsize (p_ertm_info->user_rx_pool_id) - (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN); + } + + /* If link is up, start the L2CAP connection */ + if (p_lcb->link_state == LST_CONNECTED) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL); + } + + /* If link is disconnecting, save link info to retry after disconnect + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + else if (p_lcb->link_state == LST_DISCONNECTING) + { + L2CAP_TRACE_DEBUG0 ("L2CAP API - link disconnecting: RETRY LATER"); + + /* Save ccb so it can be started after disconnect is finished */ + p_lcb->p_pending_ccb = p_ccb; + } + + L2CAP_TRACE_API2 ("L2CAP - L2CA_conn_req(psm: 0x%04x) returned CID: 0x%04x", psm, p_ccb->local_cid); + + /* Return the local CID as our handle */ + return (p_ccb->local_cid); +} + + +/******************************************************************************* +** +** Function L2CA_ConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, UINT16 status) +{ + return L2CA_ErtmConnectRsp (p_bd_addr, id, lcid, result, status, NULL); +} + + +/******************************************************************************* +** +** Function L2CA_ErtmConnectRsp +** +** Description Higher layers call this function to accept an incoming +** L2CAP connection, for which they had gotten an connect +** indication callback. +** +** Returns TRUE for success, FALSE for failure +** +*******************************************************************************/ +BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result, + UINT16 status, tL2CAP_ERTM_INFO *p_ertm_info) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API6 ("L2CA_ErtmConnectRsp() CID: 0x%04x Result: %d Status: %d BDA: %08x%04x p_ertm_info:0x%08x", + lcid, result, status, + (p_bd_addr[0]<<24)+(p_bd_addr[1]<<16)+(p_bd_addr[2]<<8)+p_bd_addr[3], + (p_bd_addr[4]<<8)+p_bd_addr[5], p_ertm_info); + + /* First, find the link control block */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL) + { + /* No link. Get an LCB and start link establishment */ + L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_conn_rsp"); + return (FALSE); + } + + /* Now, find the channel control block */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_conn_rsp"); + return (FALSE); + } + + /* The IDs must match */ + if (p_ccb->remote_id != id) + { + L2CAP_TRACE_WARNING2 ("L2CAP - bad id in L2CA_conn_rsp. Exp: %d Got: %d", p_ccb->remote_id, id); + return (FALSE); + } + + if (p_ertm_info) + { + p_ccb->ertm_info = *p_ertm_info; + + /* Replace default indicators with the actual default pool */ + if (p_ccb->ertm_info.fcr_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID; + + if (p_ccb->ertm_info.fcr_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID; + + if (p_ccb->ertm_info.user_rx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID; + + if (p_ccb->ertm_info.user_tx_pool_id == L2CAP_DEFAULT_ERM_POOL_ID) + p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID; + + p_ccb->max_rx_mtu = GKI_get_pool_bufsize (p_ertm_info->user_rx_pool_id) - (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN); + } + + if (result == L2CAP_CONN_OK) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL); + } + else + { + tL2C_CONN_INFO conn_info; + + conn_info.l2cap_result = result; + conn_info.l2cap_status = status; + + if (result == L2CAP_CONN_PENDING) + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, &conn_info); + else + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_ConfigReq +** +** Description Higher layers call this function to send configuration. +** +** Note: The FCR options of p_cfg are not used. +** +** Returns TRUE if configuration sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API5 ("L2CA_ConfigReq() CID 0x%04x: fcr_present:%d (mode %d) mtu_present:%d (%d)", + cid, p_cfg->fcr_present, p_cfg->fcr.mode, p_cfg->mtu_present, p_cfg->mtu); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_cfg_req, CID: %d", cid); + return (FALSE); + } + + /* We need to have at least one mode type common with the peer */ + if (!l2c_fcr_adj_our_req_options(p_ccb, p_cfg)) + return (FALSE); + + /* Don't adjust FCR options if not used */ + if ((!p_cfg->fcr_present)||(p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE)) + { + /* FCR and FCS options are not used in basic mode */ + p_cfg->fcs_present = FALSE; + p_cfg->ext_flow_spec_present = FALSE; + + if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) + { + L2CAP_TRACE_WARNING1 ("L2CAP - adjust MTU: %u too large", p_cfg->mtu); + p_cfg->mtu = L2CAP_MTU_SIZE; + } + } + + /* Save the adjusted configuration in case it needs to be used for renegotiation */ + p_ccb->our_cfg = *p_cfg; + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_REQ, p_cfg); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_ConfigRsp +** +** Description Higher layers call this function to send a configuration +** response. +** +** Returns TRUE if configuration response sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API6 ("L2CA_ConfigRsp() CID: 0x%04x Result: %d MTU present:%d Flush TO:%d FCR:%d FCS:%d", + cid, p_cfg->result, p_cfg->mtu_present, p_cfg->flush_to_present, p_cfg->fcr_present, p_cfg->fcs_present); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_cfg_rsp, CID: %d", cid); + return (FALSE); + } + + if ( (p_cfg->result == L2CAP_CFG_OK) || (p_cfg->result == L2CAP_CFG_PENDING) ) + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP, p_cfg); + else + { + p_cfg->fcr_present = FALSE; /* FCR options already negotiated before this point */ + + /* Clear out any cached options that are being returned as an error (excluding FCR) */ + if (p_cfg->mtu_present) + p_ccb->peer_cfg.mtu_present = FALSE; + if (p_cfg->flush_to_present) + p_ccb->peer_cfg.flush_to_present = FALSE; + if (p_cfg->qos_present) + p_ccb->peer_cfg.qos_present = FALSE; + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP_NEG, p_cfg); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_DisconnectReq +** +** Description Higher layers call this function to disconnect a channel. +** +** Returns TRUE if disconnect sent, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_DisconnectReq (UINT16 cid) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API1 ("L2CA_DisconnectReq() CID: 0x%04x", cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_disc_req, CID: %d", cid); + return (FALSE); + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_DisconnectRsp +** +** Description Higher layers call this function to acknowledge the +** disconnection of a channel. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN L2CA_DisconnectRsp (UINT16 cid) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API1 ("L2CA_DisconnectRsp() CID: 0x%04x", cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_disc_rsp, CID: %d", cid); + return (FALSE); + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_Ping +** +** Description Higher layers call this function to send an echo request. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_callback) +{ + tL2C_LCB *p_lcb; + + L2CAP_TRACE_API6 ("L2CA_Ping() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + return (FALSE); + + /* First, see if we already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL) + { + /* No link. Get an LCB and start link establishment */ + if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_ping"); + return (FALSE); + } + if (l2cu_create_conn(p_lcb) == FALSE) + { + return (FALSE); + } + + p_lcb->p_echo_rsp_cb = p_callback; + + return (TRUE); + } + + /* We only allow 1 ping outstanding at a time */ + if (p_lcb->p_echo_rsp_cb != NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - rejected second L2CA_ping"); + return (FALSE); + } + + /* Have a link control block. If link is disconnecting, tell user to retry later */ + if (p_lcb->link_state == LST_DISCONNECTING) + { + L2CAP_TRACE_WARNING0 ("L2CAP - L2CA_ping rejected - link disconnecting"); + return (FALSE); + } + + /* Save address of callback */ + p_lcb->p_echo_rsp_cb = p_callback; + + if (p_lcb->link_state == LST_CONNECTED) + { + l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */ + l2cu_send_peer_echo_req (p_lcb, NULL, 0); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT); + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_Echo +** +** Description Higher layers call this function to send an echo request +** with application-specific data. +** +** Returns TRUE if echo request sent, else FALSE. +** +*******************************************************************************/ +BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback) +{ + tL2C_LCB *p_lcb; + UINT8 *pp; + + L2CAP_TRACE_API2 ("L2CA_Echo() BDA: %08X%04X", + ((p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + (p_bd_addr[3])), + ((p_bd_addr[4] << 8) + (p_bd_addr[5]))); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + return (FALSE); + + if ((memcmp(BT_BD_ANY, p_bd_addr, BD_ADDR_LEN) == 0) && (p_data == NULL)) + { + /* Only register callback without sending message. */ + l2cb.p_echo_data_cb = p_callback; + return TRUE; + } + + /* We assume the upper layer will call this function only when the link is established. */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr)) == NULL) + { + L2CAP_TRACE_ERROR0 ("L2CA_Echo ERROR : link not established"); + return FALSE; + } + + if (p_lcb->link_state != LST_CONNECTED) + { + L2CAP_TRACE_ERROR0 ("L2CA_Echo ERROR : link is not connected"); + return FALSE; + } + + /* Save address of callback */ + l2cb.p_echo_data_cb = p_callback; + + /* Set the pointer to the beginning of the data */ + pp = (UINT8 *)(p_data + 1) + p_data->offset; + l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */ + l2cu_send_peer_echo_req (p_lcb, pp, p_data->len); + + return (TRUE); + +} + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeout +** +** 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 TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout takes effect after at least 1 channel has been +** established and removed. L2CAP maintains its own timer from +** whan a connection is established till the first channel is +** set up. +*******************************************************************************/ +BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, BOOLEAN is_global) +{ + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + + if (is_global) + { + l2cb.idle_timeout = timeout; + } + else + { + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_SetIdleTimeout, CID: %d", cid); + return (FALSE); + } + + p_lcb = p_ccb->p_lcb; + + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) + p_lcb->idle_timeout = timeout; + else + return (FALSE); + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetIdleTimeoutByBdAddr +** +** Description Higher layers call this function to set the idle timeout for +** a connection. 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. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout) +{ + tL2C_LCB *p_lcb; + + if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) + { + p_lcb = l2cu_find_lcb_by_bd_addr( bd_addr ); + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) + p_lcb->idle_timeout = timeout; + else + return (FALSE); + } + else + { + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) + { + p_lcb->idle_timeout = timeout; + } + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetTraceLevel +** +** Description This function sets the trace level for L2CAP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 L2CA_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + l2cb.l2cap_trace_level = new_level; + + return (l2cb.l2cap_trace_level); +} + + +/******************************************************************************* +** +** Function L2CA_SetDesireRole +** +** Description This function sets the desire role for L2CAP. +** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on +** HciCreateConnection. +** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on +** HciCreateConnection. +** +** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE), +** the desire role is set to the new value. Otherwise, it is not changed. +** +** Returns the new (current) role +** +*******************************************************************************/ +UINT8 L2CA_SetDesireRole (UINT8 new_role) +{ + L2CAP_TRACE_API2 ("L2CA_SetDesireRole() new:x%x, disallow_switch:%d", + new_role, l2cb.disallow_switch); + + if (L2CAP_ROLE_CHECK_SWITCH != (L2CAP_ROLE_CHECK_SWITCH & new_role)) + { + /* do not process the allow_switch when both bits are set */ + if (new_role & L2CAP_ROLE_ALLOW_SWITCH) + { + l2cb.disallow_switch = FALSE; + } + if (new_role & L2CAP_ROLE_DISALLOW_SWITCH) + { + l2cb.disallow_switch = TRUE; + } + } + + if (new_role == HCI_ROLE_MASTER || new_role == HCI_ROLE_SLAVE) + l2cb.desire_role = new_role; + + return (l2cb.desire_role); +} + +/******************************************************************************* +** +** Function L2CA_LocalLoopbackReq +** +** Description This function sets up a CID for local loopback +** +** Returns CID of 0 if none. +** +*******************************************************************************/ +UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API2 ("L2CA_LocalLoopbackReq() PSM: %d Handle: 0x%04x", psm, handle); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING0 ("L2CAP loop req - BTU not ready"); + return (0); + } + + /* Fail if the PSM is not registered */ + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm); + return (0); + } + + if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_conn_req"); + return (0); + } + + p_lcb->link_state = LST_CONNECTED; + p_lcb->handle = handle; + + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_conn_req"); + return (0); + } + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + p_ccb->chnl_state = CST_OPEN; + p_ccb->remote_cid = p_ccb->local_cid; + p_ccb->config_done = CFG_DONE_MASK; + + /* Return the local CID as our handle */ + return (p_ccb->local_cid); +} + +/******************************************************************************* +** +** Function L2CA_SetAclPriority +** +** Description Sets the transmission priority for a channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority) +{ + L2CAP_TRACE_API6 ("L2CA_SetAclPriority() bdaddr: %02x%02x%02x%02x%04x, priority:%d", + bd_addr[0], bd_addr[1], bd_addr[2], + bd_addr[3], (bd_addr[4] << 8) + bd_addr[5], priority); + + return (l2cu_set_acl_priority(bd_addr, priority, FALSE)); +} + +/******************************************************************************* +** +** Function L2CA_FlowControl +** +** Description Higher layers call this function to flow control a channel. +** +** data_enabled - TRUE data flows, FALSE data is stopped +** +** Returns TRUE if valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled) +{ + tL2C_CCB *p_ccb; + BOOLEAN on_off = !data_enabled; + + L2CAP_TRACE_API2 ("L2CA_FlowControl(%d) CID: 0x%04x", on_off, cid); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING2 ("L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x data_enabled: %d", cid, data_enabled); + return (FALSE); + } + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) + { + L2CAP_TRACE_EVENT1 ("L2CA_FlowControl() invalid mode:%d", p_ccb->peer_cfg.fcr.mode); + return (FALSE); + } + if (p_ccb->fcrb.local_busy != on_off) + { + p_ccb->fcrb.local_busy = on_off; + + if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) ) + { + if (on_off) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT); + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SendTestSFrame +** +** Description Higher layers call this function to send a test S-frame. +** +** Returns TRUE if valid Channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SendTestSFrame (UINT16 cid, UINT8 sup_type, UINT8 back_track) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API3 ("L2CA_SendTestSFrame() CID: 0x%04x Type: 0x%02x back_track: %u", cid, sup_type, back_track); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid); + return (FALSE); + } + + if ( (p_ccb->chnl_state != CST_OPEN) || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) + return (FALSE); + + p_ccb->fcrb.next_seq_expected -= back_track; + + l2c_fcr_send_S_frame (p_ccb, (UINT16)(sup_type & 3), (UINT16)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT))); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_SetTxPriority +** +** Description Sets the transmission priority for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API2 ("L2CA_SetTxPriority() CID: 0x%04x, priority:%d", cid, priority); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_SetTxPriority, CID: %d", cid); + return (FALSE); + } + + /* it will update the order of CCB in LCB by priority and update round robin service variables */ + l2cu_change_pri_ccb (p_ccb, priority); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetChnlDataRate +** +** Description Sets the tx/rx data rate for a channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API3 ("L2CA_SetChnlDataRate() CID: 0x%04x, tx:%d, rx:%d", cid, tx, rx); + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_SetChnlDataRate, CID: %d", cid); + return (FALSE); + } + + p_ccb->tx_data_rate = tx; + p_ccb->rx_data_rate = rx; + + /* Adjust channel buffer allocation */ + l2c_link_adjust_chnl_allocation (); + + return(TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetFlushTimeout +** +** Description This function set the automatic flush time out in Baseband +** for ACL-U packets. +** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY +** then the flush time out will be applied to all ACL link. +** FlushTimeout: flush time out in ms +** 0x0000 : No automatic flush +** L2CAP_NO_RETRANSMISSION : No retransmission +** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5) +** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot). +** Otherwise, return FALSE. +** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush +** +** Returns TRUE if command succeeded, FALSE if failed +** +** NOTE This flush timeout applies to all logical channels active on the +** ACL link. +*******************************************************************************/ +BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout) +{ + tL2C_LCB *p_lcb; + UINT16 hci_flush_to; + UINT32 temp; + + /* no automatic flush (infinite timeout) */ + if (flush_tout == 0x0000) + { + hci_flush_to = flush_tout; + flush_tout = L2CAP_NO_AUTOMATIC_FLUSH; + } + /* no retransmission */ + else if (flush_tout == L2CAP_NO_RETRANSMISSION) + { + /* not mandatory range for controller */ + /* Packet is flushed before getting any ACK/NACK */ + /* To do this, flush timeout should be 1 baseband slot */ + hci_flush_to = flush_tout; + } + /* no automatic flush (infinite timeout) */ + else if (flush_tout == L2CAP_NO_AUTOMATIC_FLUSH) + { + hci_flush_to = 0x0000; + } + else + { + /* convert L2CAP flush_to to 0.625 ms units, with round */ + temp = (((UINT32)flush_tout * 8) + 3) / 5; + + /* if L2CAP flush_to within range of HCI, set HCI flush timeout */ + if (temp > HCI_MAX_AUTO_FLUSH_TOUT) + { + L2CAP_TRACE_WARNING1("WARNING L2CA_SetFlushTimeout timeout(0x%x) is out of range", flush_tout); + return FALSE; + } + else + { + hci_flush_to = (UINT16)temp; + } + } + + if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) + { + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr); + + if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) + { + if (p_lcb->link_flush_tout != flush_tout) + { + p_lcb->link_flush_tout = flush_tout; + + L2CAP_TRACE_API4 ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]", + flush_tout, bd_addr[3], bd_addr[4], bd_addr[5]); + + if (!btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to)) + return (FALSE); + } + } + else + { + L2CAP_TRACE_WARNING3 ("WARNING L2CA_SetFlushTimeout No lcb for bd_addr [...;%02x%02x%02x]", + bd_addr[3], bd_addr[4], bd_addr[5]); + return (FALSE); + } + } + else + { + int xx; + p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) + { + if (p_lcb->link_flush_tout != flush_tout) + { + p_lcb->link_flush_tout = flush_tout; + + L2CAP_TRACE_API4 ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]", + flush_tout, p_lcb->remote_bd_addr[3], + p_lcb->remote_bd_addr[4], p_lcb->remote_bd_addr[5]); + + if (!btsnd_hcic_write_auto_flush_tout(p_lcb->handle, hci_flush_to)) + return (FALSE); + } + } + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_GetPeerFeatures +** +** Description Get a peers features and fixed channel map +** +** Parameters: BD address of the peer +** Pointers to features and channel mask storage area +** +** Return value: TRUE if peer is connected +** +*******************************************************************************/ +BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask) +{ + tL2C_LCB *p_lcb; + + /* We must already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr)) == NULL) + { + L2CAP_TRACE_WARNING2 ("L2CA_GetPeerFeatures() No BDA: %08x%04x", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5]); + return (FALSE); + } + + L2CAP_TRACE_API4 ("L2CA_GetPeerFeatures() BDA: %08x%04x ExtFea: 0x%08x Chnl_Mask[0]: 0x%02x", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], p_lcb->peer_ext_fea, p_lcb->peer_chnl_mask[0]); + + *p_ext_feat = p_lcb->peer_ext_fea; + + memcpy (p_chnl_mask, p_lcb->peer_chnl_mask, L2CAP_FIXED_CHNL_ARRAY_SIZE); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_GetBDAddrbyHandle +** +** Description Get BD address for the given HCI handle +** +** Parameters: HCI handle +** BD address of the peer +** +** Return value: TRUE if found lcb for the given handle, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb = NULL; + BOOLEAN found_dev = FALSE; + + p_lcb = l2cu_find_lcb_by_handle (handle); + if (p_lcb) + { + found_dev = TRUE; + memcpy (bd_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + + return found_dev; +} + +/******************************************************************************* +** +** Function L2CA_GetChnlFcrMode +** +** Description Get the channel FCR mode +** +** Parameters: Local CID +** +** Return value: Channel mode +** +*******************************************************************************/ +UINT8 L2CA_GetChnlFcrMode (UINT16 lcid) +{ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, lcid); + + if (p_ccb) + { + L2CAP_TRACE_API1 ("L2CA_GetChnlFcrMode() returns mode %d", p_ccb->peer_cfg.fcr.mode); + return (p_ccb->peer_cfg.fcr.mode); + } + + L2CAP_TRACE_API0 ("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE"); + return (L2CAP_FCR_BASIC_MODE); +} + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function L2CA_RegisterFixedChannel +** +** Description Register a fixed channel. +** +** Parameters: Fixed Channel # +** Channel Callbacks and config +** +** Return value: - +** +*******************************************************************************/ +BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg) +{ + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) ) + { + L2CAP_TRACE_ERROR1 ("L2CA_RegisterFixedChannel() Invalid CID: 0x%04x", fixed_cid); + + return (FALSE); + } + + l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = *p_freg; + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_ConnectFixedChnl +** +** Description Connect an fixed signalling channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** +** Return value: TRUE if connection started +** +*******************************************************************************/ +BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; +#if BLE_INCLUDED == TRUE + UINT16 reason; +#endif + + L2CAP_TRACE_API3 ("L2CA_ConnectFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + + /* Check CID is valid and registered */ + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) + { + L2CAP_TRACE_ERROR1 ("L2CA_ConnectFixedChnl() Invalid CID: 0x%04x", fixed_cid); + return (FALSE); + } + + /* Fail if BT is not yet up */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - BTU not ready", fixed_cid); + return (FALSE); + } + + /* If we already have a link to the remote, check if it supports that CID */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) != NULL) + { + if (!(p_lcb->peer_chnl_mask[0] & (1 << fixed_cid))) + { + L2CAP_TRACE_EVENT3 ("L2CA_ConnectFixedChnl() CID: 0x%04x BDA: %08x%04x not supported", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return (FALSE); + } + /* Get a CCB and link the lcb to it */ + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - LCB but no CCB", fixed_cid); + return (FALSE); + } +#if BLE_INCLUDED == TRUE + reason = (p_lcb->is_ble_link) ? 1: 0; + (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, reason); +#else + (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, 0); +#endif + return (TRUE); + } + + /* No link. Get an LCB and start link establishment */ + if ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - no LCB", fixed_cid); + return (FALSE); + } + + /* Get a CCB and link the lcb to it */ + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES; + L2CAP_TRACE_WARNING1 ("L2CA_ConnectFixedChnl(0x%04x) - no CCB", fixed_cid); + l2cu_release_lcb (p_lcb); + return (FALSE); + } + + return (l2cu_create_conn(p_lcb)); +} + +/******************************************************************************* +** +** Function L2CA_SendFixedChnlData +** +** Description Write data on a fixed channel. +** +** Parameters: Fixed CID +** BD Address of remote +** Pointer to buffer of type BT_HDR +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf) +{ + tL2C_LCB *p_lcb; + + L2CAP_TRACE_API3 ("L2CA_SendFixedChnlData() CID: 0x%04x BDA: %08x%04x", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + + /* Check CID is valid and registered */ + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) + { + L2CAP_TRACE_ERROR1 ("L2CA_SendFixedChnlData() Invalid CID: 0x%04x", fixed_cid); + return (L2CAP_DW_FAILED); + } + + /* Fail if BT is not yet up */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid); + return (L2CAP_DW_FAILED); + } + + /* We need to have a link up */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData(0x%04x) - no LCB", fixed_cid); + return (L2CAP_DW_FAILED); + } + + if ((p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)) == 0) + { + L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData() - peer does not support fixed chnl: 0x%04x", fixed_cid); + return (L2CAP_DW_FAILED); + } + + p_buf->event = 0; + p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED; + + if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) + { + if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + L2CAP_TRACE_WARNING1 ("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x", fixed_cid); + return (L2CAP_DW_FAILED); + } + } + + l2c_enqueue_peer_data (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf); + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + /* If there is no dynamic CCB on the link, restart the idle timer each time something is sent */ + if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) + { + l2cu_no_dynamic_ccbs (p_lcb); + } + + return (L2CAP_DW_SUCCESS); +} + +/******************************************************************************* +** +** Function L2CA_RemoveFixedChnl +** +** Description Remove a fixed channel to a remote device. +** +** Parameters: Fixed CID +** BD Address of remote +** Idle timeout to use (or 0xFFFF if don't care) +** +** Return value: TRUE if channel removed +** +*******************************************************************************/ +BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + /* Check CID is valid and registered */ + if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) + || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) + { + L2CAP_TRACE_ERROR1 ("L2CA_RemoveFixedChnl() Invalid CID: 0x%04x", fixed_cid); + return (FALSE); + } + + /* Is a fixed channel connected to the remote BDA ?*/ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda); + if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) + { + L2CAP_TRACE_WARNING3 ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return (FALSE); + } + + L2CAP_TRACE_API3 ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + + /* Release the CCB, starting an inactivity timeout on the LCB if no other CCBs exist */ + p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]; + + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = NULL; + p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST; + l2cu_release_ccb (p_ccb); + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_SetFixedChannelTout +** +** Description Higher layers call this function to set the idle timeout for +** a fixed channel. 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. +** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY, +** then the idle timeouts for all active l2cap links will be +** changed. +** +** Returns TRUE if command succeeded, FALSE if failed +** +*******************************************************************************/ +BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout) +{ + tL2C_LCB *p_lcb; + + /* Is a fixed channel connected to the remote BDA ?*/ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda); + if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) + { + L2CAP_TRACE_WARNING3 ("L2CA_SetFixedChannelTout() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return (FALSE); + } + + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->fixed_chnl_idle_tout = idle_tout; + + if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) + { + /* If there are no dynamic CCBs, (re)start the idle timer in case we changed it */ + l2cu_no_dynamic_ccbs (p_lcb); + } + + return (TRUE); +} + +#endif /* #if (L2CAP_NUM_FIXED_CHNLS > 0) */ + +/******************************************************************************* +** +** Function L2CA_GetCurrentConfig +** +** Description This function returns configurations of L2CAP channel +** pp_our_cfg : pointer of our saved configuration options +** p_our_cfg_bits : valid config in bitmap +** pp_peer_cfg: pointer of peer's saved configuration options +** p_peer_cfg_bits : valid config in bitmap +** +** Returns TRUE if successful +** +*******************************************************************************/ +BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid, + tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits, + tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits) +{ + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API1 ("L2CA_GetCurrentConfig() CID: 0x%04x", lcid); + + p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + + if (p_ccb) + { + *pp_our_cfg = &(p_ccb->our_cfg); + + /* convert valid config items into bitmap */ + *p_our_cfg_bits = 0; + if (p_ccb->our_cfg.mtu_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU; + if (p_ccb->our_cfg.qos_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS; + if (p_ccb->our_cfg.flush_to_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO; + if (p_ccb->our_cfg.fcr_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR; + if (p_ccb->our_cfg.fcs_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS; + if (p_ccb->our_cfg.ext_flow_spec_present) + *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC; + + *pp_peer_cfg = &(p_ccb->peer_cfg); + *p_peer_cfg_bits = p_ccb->peer_cfg_bits; + + return TRUE; + } + else + { + L2CAP_TRACE_ERROR1 ("No CCB for CID:0x%04x", lcid); + return FALSE; + } +} + +/******************************************************************************* +** +** Function L2CA_RegForNoCPEvt +** +** Description Register callback for Number of Completed Packets event. +** +** Input Param p_cb - callback for Number of completed packets event +** p_bda - BT address of remote device +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda) +{ + tL2C_LCB *p_lcb; + + /* Find the link that is associated with this remote bdaddr */ + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda); + + /* If no link for this handle, nothing to do. */ + if (!p_lcb) + return FALSE; + + p_lcb->p_nocp_cb = p_cb; + + return TRUE; +} + +/******************************************************************************* +** +** Function L2CA_DataWrite +** +** Description Higher layers call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data) +{ + L2CAP_TRACE_API2 ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len); + return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED); +} + +/******************************************************************************* +** +** Function L2CA_SetChnlFlushability +** +** Description Higher layers call this function to set a channels +** flushability flags +** +** Returns TRUE if CID found, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable) +{ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + + tL2C_CCB *p_ccb; + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_SetChnlFlushability, CID: %d", cid); + return (FALSE); + } + + p_ccb->is_flushable = is_flushable; + + L2CAP_TRACE_API2 ("L2CA_SetChnlFlushability() CID: 0x%04x is_flushable: %d", cid, is_flushable); + +#endif + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_DataWriteEx +** +** Description Higher layers call this function to write data with extended +** flags. +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags) +{ + L2CAP_TRACE_API3 ("L2CA_DataWriteEx() CID: 0x%04x Len: %d Flags:0x%04X", + cid, p_data->len, flags); + return l2c_data_write (cid, p_data, flags); +} + +/******************************************************************************* +** +** Function L2CA_FlushChannel +** +** Description This function flushes none, some or all buffers queued up +** for xmission for a particular CID. If called with +** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number +** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff) +** flushes all buffers. All other values specifies the maximum +** buffers to flush. +** +** Returns Number of buffers left queued for that CID +** +*******************************************************************************/ +UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush) +{ + tL2C_CCB *p_ccb; + tL2C_LCB *p_lcb; + UINT16 num_left = 0, + num_flushed1 = 0, + num_flushed2 = 0; + BT_HDR *p_buf1, *p_buf; + + p_ccb = l2cu_find_ccb_by_cid(NULL, lcid); + + if ( !p_ccb || ((p_lcb = p_ccb->p_lcb) == NULL) ) + { + L2CAP_TRACE_WARNING1 ("L2CA_FlushChannel() abnormally returning 0 CID: 0x%04x", lcid); + return (0); + } + + if (num_to_flush != L2CAP_FLUSH_CHANS_GET) + { + L2CAP_TRACE_API4 ("L2CA_FlushChannel (FLUSH) CID: 0x%04x NumToFlush: %d QC: %u pFirst: 0x%08x", + lcid, num_to_flush, p_ccb->xmit_hold_q.count, p_ccb->xmit_hold_q.p_first); + } + else + { + L2CAP_TRACE_API1 ("L2CA_FlushChannel (QUERY) CID: 0x%04x", lcid); + } + + /* Cannot flush eRTM buffers once they have a sequence number */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) + { +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if (num_to_flush != L2CAP_FLUSH_CHANS_GET) + { + /* If the controller supports enhanced flush, flush the data queued at the controller */ + if ( (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ())) + && (BTM_GetNumScoLinks() == 0) ) + { + if ( l2cb.is_flush_active == FALSE ) + { + l2cb.is_flush_active = TRUE; + + /* The only packet type defined - 0 - Automatically-Flushable Only */ + btsnd_hcic_enhanced_flush (p_lcb->handle, 0); + } + } + } +#endif + + p_buf = (BT_HDR *)p_lcb->link_xmit_data_q.p_first; + + /* First flush the number we are asked to flush */ + while ((p_buf != NULL) && (num_to_flush != 0)) + { + /* Do not flush other CIDs or partial segments */ + if ( (p_buf->layer_specific == 0) && (p_buf->event == lcid) ) + { + p_buf1 = p_buf; + p_buf = (BT_HDR *)GKI_getnext (p_buf); + num_to_flush--; + num_flushed1++; + + GKI_remove_from_queue (&p_lcb->link_xmit_data_q, p_buf1); + GKI_freebuf (p_buf1); + } + else + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + } + + /* If needed, flush buffers in the CCB xmit hold queue */ + while ( (num_to_flush != 0) && (p_ccb->xmit_hold_q.count != 0) ) + { + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q); + if (p_buf) + GKI_freebuf (p_buf); + num_to_flush--; + num_flushed2++; + } + + /* If app needs to track all packets, call him */ + if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (num_flushed2) ) + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, num_flushed2); + + /* Now count how many are left */ + p_buf = (BT_HDR *)p_lcb->link_xmit_data_q.p_first; + + while (p_buf != NULL) + { + if (p_buf->event == lcid) + num_left++; + + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + + /* Add in the number in the CCB xmit queue */ + num_left += p_ccb->xmit_hold_q.count; + + /* Return the local number of buffers left for the CID */ + L2CAP_TRACE_DEBUG3 ("L2CA_FlushChannel() flushed: %u + %u, num_left: %u", num_flushed1, num_flushed2, num_left); + + /* If we were congested, and now we are not, tell the app */ + l2cu_check_channel_congestion (p_ccb); + + return (num_left); +} + diff --git a/stack/l2cap/l2c_ble.c b/stack/l2cap/l2c_ble.c new file mode 100644 index 0000000..f464ff3 --- /dev/null +++ b/stack/l2cap/l2c_ble.c @@ -0,0 +1,599 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains functions relating to BLE management. + * + ******************************************************************************/ + +#include +#include "bt_target.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "btu.h" +#include "btm_int.h" +#include "hcimsgs.h" + +#if (BLE_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function L2CA_CancelBleConnectReq +** +** Description Cancel a pending connection attempt to a BLE device. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if connection was cancelled +** +*******************************************************************************/ +BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda) +{ + tL2C_LCB *p_lcb; + + /* There can be only one BLE connection request outstanding at a time */ + if (!l2cb.is_ble_connecting) + { + L2CAP_TRACE_WARNING0 ("L2CA_CancelBleConnectReq - no connection pending"); + return(FALSE); + } + + if (memcmp (rem_bda, l2cb.ble_connecting_bda, BD_ADDR_LEN)) + { + L2CAP_TRACE_WARNING4 ("L2CA_CancelBleConnectReq - different BDA Connecting: %08x%04x Cancel: %08x%04x", + (l2cb.ble_connecting_bda[0]<<24)+(l2cb.ble_connecting_bda[1]<<16)+(l2cb.ble_connecting_bda[2]<<8)+l2cb.ble_connecting_bda[3], + (l2cb.ble_connecting_bda[4]<<8)+l2cb.ble_connecting_bda[5], + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + + return(FALSE); + } + + if (btsnd_hcic_ble_create_conn_cancel()) + { + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) != NULL) + { + p_lcb->disc_reason = L2CAP_CONN_CANCEL; + l2cu_release_lcb (p_lcb); + } + + l2cb.is_ble_connecting = FALSE; + btm_ble_update_bg_state(); + btm_ble_resume_bg_conn(NULL, TRUE); + + return(TRUE); + } + else + return(FALSE); +} + + +/******************************************************************************* +** +** Function L2CA_UpdateBleConnParams +** +** Description Update BLE connection parameters. +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bda, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout) +{ + tL2C_LCB *p_lcb; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) + { + L2CAP_TRACE_WARNING2 ("L2CA_UpdateBleConnParams - unknown BD_ADDR %08x%04x", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return(FALSE); + } + + if (!p_lcb->is_ble_link) + { + L2CAP_TRACE_WARNING2 ("L2CA_UpdateBleConnParams - BD_ADDR %08x%04x not LE", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return(FALSE); + } + + if (p_lcb->link_role == HCI_ROLE_MASTER) + btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, min_int, max_int, latency, timeout, 0, 0); + else + l2cu_send_peer_ble_par_req (p_lcb, min_int, max_int, latency, timeout); + + return(TRUE); +} + + +/******************************************************************************* +** +** Function L2CA_EnableUpdateBleConnParams +** +** Description Enable or disable update based on the request from the peer +** +** Parameters: BD Address of remote +** +** Return value: TRUE if update started +** +*******************************************************************************/ +BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable) +{ + tL2C_LCB *p_lcb; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) + { + L2CAP_TRACE_WARNING2 ("L2CA_EnableUpdateBleConnParams - unknown BD_ADDR %08x%04x", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5]); + return (FALSE); + } + + L2CAP_TRACE_API4 ("L2CA_EnableUpdateBleConnParams - BD_ADDR %08x%04x enable %d current upd state %d", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5], enable, p_lcb->upd_disabled); + + if (!p_lcb->is_ble_link || (p_lcb->link_role != HCI_ROLE_MASTER)) + { + L2CAP_TRACE_WARNING3 ("L2CA_EnableUpdateBleConnParams - BD_ADDR %08x%04x not LE or not master %d", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], (rem_bda[4]<<8)+rem_bda[5], p_lcb->link_role); + return (FALSE); + } + + if (enable) + { + /* application allows to do update, if we were delaying one do it now, otherwise + just mark lcb that updates are enabled */ + if (p_lcb->upd_disabled == UPD_PENDING) + { + btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, p_lcb->min_interval, p_lcb->max_interval, + p_lcb->latency, p_lcb->timeout, 0, 0); + p_lcb->upd_disabled = UPD_UPDATED; + } + else + { + p_lcb->upd_disabled = UPD_ENABLED; + } + } + else + { + /* application requests to disable parameters update. If parameters are already updated, lets set them + up to what has been requested during connection establishement */ + if (p_lcb->upd_disabled == UPD_UPDATED) + { + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (rem_bda); + + btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, + (UINT16)((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.min_conn_int : L2CAP_LE_INT_MIN), + (UINT16)((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.max_conn_int : L2CAP_LE_INT_MAX), + (UINT16)((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.slave_latency : 0), + (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.supervision_tout : L2CAP_LE_TIMEOUT_MAX), + 0, 0); + } + p_lcb->upd_disabled = UPD_DISABLED; + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_GetBleConnRole +** +** Description This function returns the connection role. +** +** Returns link role. +** +*******************************************************************************/ +UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr) +{ + UINT8 role = HCI_ROLE_UNKNOWN; + + tL2C_LCB *p_lcb; + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr)) != NULL) + role = p_lcb->link_role; + + return role; +} +/******************************************************************************* +** +** Function L2CA_GetDisconnectReason +** +** Description This function returns the disconnect reason code. +** +** Returns disconnect reason +** +*******************************************************************************/ +UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda) +{ + tL2C_LCB *p_lcb; + UINT16 reason = 0; + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda)) != NULL) + reason = p_lcb->disc_reason; + + L2CAP_TRACE_DEBUG1 ("L2CA_GetDisconnectReason=%d ",reason); + + return reason; +} + +/******************************************************************************* +** +** Function l2cble_scanner_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received while we are a scanner (so we are master). +** +** Returns void +** +*******************************************************************************/ +void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + tL2C_LCB *p_lcb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda); + + L2CAP_TRACE_DEBUG5 ("l2cble_scanner_conn_comp: HANDLE=%d addr_type=%d conn_interval=%d slave_latency=%d supervision_tout=%d", + handle, type, conn_interval, conn_latency, conn_timeout); + + l2cb.is_ble_connecting = FALSE; + + p_dev_rec->device_type = BT_DEVICE_TYPE_BLE; + p_dev_rec->ble.ble_addr_type = type; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bda); + + /* If we don't have one, create one. this is auto connection complete. */ + if (!p_lcb) + { + p_lcb = l2cu_allocate_lcb (bda, FALSE); + if (!p_lcb) + { + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_ERROR0 ("l2cble_scanner_conn_comp - failed to allocate LCB"); + return; + } + else + { + if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_WARNING0 ("l2cble_scanner_conn_comp - LCB but no CCB"); + return ; + } + } + } + else if (p_lcb->link_state != LST_CONNECTING) + { + L2CAP_TRACE_ERROR1 ("L2CAP got BLE scanner conn_comp in bad state: %d", p_lcb->link_state); + return; + } + btu_stop_timer(&p_lcb->timer_entry); + + /* Save the handle */ + p_lcb->handle = handle; + + /* Connected OK. Change state to connected, we were scanning so we are master */ + p_lcb->link_state = LST_CONNECTED; + p_lcb->link_role = HCI_ROLE_MASTER; + p_lcb->is_ble_link = TRUE; + + /* If there are any preferred connection parameters, set them now */ + if ( (p_dev_rec->conn_params.min_conn_int >= L2CAP_LE_INT_MIN ) && + (p_dev_rec->conn_params.min_conn_int <= L2CAP_LE_INT_MAX ) && + (p_dev_rec->conn_params.max_conn_int >= L2CAP_LE_INT_MIN ) && + (p_dev_rec->conn_params.max_conn_int <= L2CAP_LE_INT_MAX ) && + (p_dev_rec->conn_params.slave_latency <= L2CAP_LE_LATENCY_MAX ) && + (p_dev_rec->conn_params.supervision_tout >= L2CAP_LE_TIMEOUT_MIN) && + (p_dev_rec->conn_params.supervision_tout <= L2CAP_LE_TIMEOUT_MAX) && + ((conn_interval < p_dev_rec->conn_params.min_conn_int && + p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) || + (conn_interval > p_dev_rec->conn_params.max_conn_int) || + (conn_latency > p_dev_rec->conn_params.slave_latency) || + (conn_timeout > p_dev_rec->conn_params.supervision_tout))) + { + L2CAP_TRACE_ERROR5 ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d", + handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, + p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout); + + btsnd_hcic_ble_upd_ll_conn_params (handle, + p_dev_rec->conn_params.min_conn_int, + p_dev_rec->conn_params.max_conn_int, + p_dev_rec->conn_params.slave_latency, + p_dev_rec->conn_params.supervision_tout, + 0, 0); + } + + /* Tell BTM Acl management about the link */ + btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, TRUE); + + if (p_lcb->p_echo_rsp_cb) + { + L2CAP_TRACE_ERROR0 ("l2cu_send_peer_echo_req"); + l2cu_send_peer_echo_req (p_lcb, NULL, 0); + } + + p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; + + l2cu_process_fixed_chnl_resp (p_lcb); +} + + +/******************************************************************************* +** +** Function l2cble_advertiser_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received while we are an advertiser (so we are slave). +** +** Returns void +** +*******************************************************************************/ +void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + tL2C_LCB *p_lcb; + tBTM_SEC_DEV_REC *p_dev_rec; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bda); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) + { + p_lcb = l2cu_allocate_lcb (bda, FALSE); + if (!p_lcb) + { + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_ERROR0 ("l2cble_advertiser_conn_comp - failed to allocate LCB"); + return; + } + else + { + if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); + L2CAP_TRACE_WARNING0 ("l2cble_scanner_conn_comp - LCB but no CCB"); + return ; + } + } + } + + /* Save the handle */ + p_lcb->handle = handle; + + /* Connected OK. Change state to connected, we were advertising, so we are slave */ + p_lcb->link_state = LST_CONNECTED; + p_lcb->link_role = HCI_ROLE_SLAVE; + p_lcb->is_ble_link = TRUE; + + /* Tell BTM Acl management about the link */ + p_dev_rec = btm_find_or_alloc_dev (bda); + + p_dev_rec->device_type = BT_DEVICE_TYPE_BLE; + p_dev_rec->ble.ble_addr_type = type; + + btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, TRUE); + + p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; + + l2cu_process_fixed_chnl_resp (p_lcb); +} + +/******************************************************************************* +** +** Function l2cble_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received. +** +** Returns void +** +*******************************************************************************/ +void l2cble_conn_comp(UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) +{ + if (role == HCI_ROLE_MASTER) + { + l2cble_scanner_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout); + } + else + { + l2cble_advertiser_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout); + } +} +/******************************************************************************* +** +** Function l2cble_process_sig_cmd +** +** Description This function is called when a signalling packet is received +** on the BLE signalling CID +** +** Returns void +** +*******************************************************************************/ +void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) +{ + UINT8 *p_pkt_end; + UINT8 cmd_code, id; + UINT16 cmd_len, rej_reason; + UINT16 result; + UINT16 min_interval, max_interval, latency, timeout; + + p_pkt_end = p + pkt_len; + + STREAM_TO_UINT8 (cmd_code, p); + STREAM_TO_UINT8 (id, p); + STREAM_TO_UINT16 (cmd_len, p); + + /* Check command length does not exceed packet length */ + if ((p + cmd_len) > p_pkt_end) + { + L2CAP_TRACE_WARNING3 ("L2CAP - LE - format error, pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code); + return; + } + + switch (cmd_code) + { + case L2CAP_CMD_REJECT: + case L2CAP_CMD_ECHO_RSP: + case L2CAP_CMD_INFO_RSP: + STREAM_TO_UINT16 (rej_reason, p); + break; + case L2CAP_CMD_ECHO_REQ: + case L2CAP_CMD_INFO_REQ: + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + break; + + case L2CAP_CMD_BLE_UPDATE_REQ: + STREAM_TO_UINT16 (min_interval, p); /* 0x0006 - 0x0C80 */ + STREAM_TO_UINT16 (max_interval, p); /* 0x0006 - 0x0C80 */ + STREAM_TO_UINT16 (latency, p); /* 0x0000 - 0x03E8 */ + STREAM_TO_UINT16 (timeout, p); /* 0x000A - 0x0C80 */ + /* If we are a master, the slave wants to update the parameters */ + if (p_lcb->link_role == HCI_ROLE_MASTER) + { + if (min_interval < L2CAP_LE_INT_MIN || min_interval > L2CAP_LE_INT_MAX || + max_interval < L2CAP_LE_INT_MIN || max_interval > L2CAP_LE_INT_MAX || + latency > L2CAP_LE_LATENCY_MAX || + /*(timeout >= max_interval && latency > (timeout * 10/(max_interval * 1.25) - 1)) ||*/ + timeout < L2CAP_LE_TIMEOUT_MIN || timeout > L2CAP_LE_TIMEOUT_MAX || + max_interval < min_interval) + { + result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + } + else + { + + l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_OK, id); + + p_lcb->min_interval = min_interval; + p_lcb->max_interval = max_interval; + p_lcb->latency = latency; + p_lcb->timeout = timeout; + + if (p_lcb->upd_disabled == UPD_ENABLED) + { + btsnd_hcic_ble_upd_ll_conn_params (p_lcb->handle, min_interval, max_interval, + latency, timeout, 0, 0); + p_lcb->upd_disabled = UPD_UPDATED; + } + else + { + L2CAP_TRACE_EVENT0 ("L2CAP - LE - update currently disabled"); + p_lcb->upd_disabled = UPD_PENDING; + } + } + } + else + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + break; + + case L2CAP_CMD_BLE_UPDATE_RSP: + STREAM_TO_UINT16 (result, p); + break; + + default: + L2CAP_TRACE_WARNING1 ("L2CAP - LE - unknown cmd code: %d", cmd_code); + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + return; + } +} + + +/******************************************************************************* +** +** Function l2cble_create_conn +** +** Description This function initiates an acl connection via HCI +** +** Returns TRUE if successful, FALSE if connection not started. +** +*******************************************************************************/ +BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_lcb->remote_bd_addr); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT16 scan_int, scan_win; + + /* There can be only one BLE connection request outstanding at a time */ + if (l2cb.is_ble_connecting) + { + L2CAP_TRACE_WARNING0 ("L2CAP - LE - cannot start new connection, already connecting"); + return(FALSE); + } + + p_lcb->link_state = LST_CONNECTING; + l2cb.is_ble_connecting = TRUE; + + memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN); + btm_ble_suspend_bg_conn(); + + scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? L2CAP_LE_INT_MIN : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? L2CAP_LE_INT_MIN : p_cb->scan_win; + + if (!btsnd_hcic_ble_create_ll_conn (scan_int,/* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + FALSE, /* UINT8 white_list */ + p_lcb->ble_addr_type, /* UINT8 addr_type_peer */ + p_lcb->remote_bd_addr, /* BD_ADDR bda_peer */ + BLE_ADDR_PUBLIC, /* UINT8 addr_type_own */ + (UINT16) ((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.min_conn_int : L2CAP_LE_INT_MIN), /* UINT16 conn_int_min */ + (UINT16) ((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.max_conn_int : L2CAP_LE_INT_MIN), /* UINT16 conn_int_max */ + (UINT16) ((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.slave_latency : 0), /* UINT16 conn_latency */ + (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ? p_dev_rec->conn_params.supervision_tout : L2CAP_LE_TIMEOUT_MAX), /* UINT16 conn_timeout */ + 0, /* UINT16 min_len */ + 0)) /* UINT16 max_len */ + { + /* No buffer for connection request ? */ + l2cb.is_ble_connecting = FALSE; + p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES; + l2cu_release_lcb (p_lcb); + return(FALSE); + } + else + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_BLE_LINK_CONNECT_TOUT); + + return(TRUE); +} + +/******************************************************************************* +** +** Function l2c_link_processs_ble_num_bufs +** +** Description This function is called when a "controller buffer size" +** event is first received from the controller. It updates +** the L2CAP values. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_processs_ble_num_bufs (UINT16 num_lm_ble_bufs) +{ + l2cb.num_lm_ble_bufs = l2cb.controller_le_xmit_window = num_lm_ble_bufs; +} + +#endif /* (BLE_INCLUDED == TRUE) */ diff --git a/stack/l2cap/l2c_csm.c b/stack/l2cap/l2c_csm.c new file mode 100644 index 0000000..0d04d8a --- /dev/null +++ b/stack/l2cap/l2c_csm.c @@ -0,0 +1,1333 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP channel state machine + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "btm_int.h" +#include "btu.h" +#include "hcimsgs.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 l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); +static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data); + +#if (BT_TRACE_VERBOSE == TRUE) +static char *l2c_csm_get_event_name (UINT16 event); +#endif + +/******************************************************************************* +** +** Function l2c_csm_execute +** +** Description This function executes the state machine. +** +** Returns void +** +*******************************************************************************/ +void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + switch (p_ccb->chnl_state) + { + case CST_CLOSED: + l2c_csm_closed (p_ccb, event, p_data); + break; + + case CST_ORIG_W4_SEC_COMP: + l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data); + break; + + case CST_TERM_W4_SEC_COMP: + l2c_csm_term_w4_sec_comp (p_ccb, event, p_data); + break; + + case CST_W4_L2CAP_CONNECT_RSP: + l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data); + break; + + case CST_W4_L2CA_CONNECT_RSP: + l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data); + break; + + case CST_CONFIG: + l2c_csm_config (p_ccb, event, p_data); + break; + + case CST_OPEN: + l2c_csm_open (p_ccb, event, p_data); + break; + + case CST_W4_L2CAP_DISCONNECT_RSP: + l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data); + break; + + case CST_W4_L2CA_DISCONNECT_RSP: + l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data); + break; + + default: + break; + } +} + +/******************************************************************************* +** +** Function l2c_csm_closed +** +** Description This function handles events when the channel is in +** CLOSED state. This state exists only when the link is +** being initially established. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data; + UINT16 local_cid = p_ccb->local_cid; + tL2CA_DISCONNECT_IND_CB *disconnect_ind; + tL2CA_CONNECT_CFM_CB *connect_cfm; + + if (p_ccb->p_rcb == NULL) + { +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_ERROR2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s p_rcb == NULL", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_ERROR2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: 0x%04x p_rcb == NULL", p_ccb->local_cid, event); +#endif + return; + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) + { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) + { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: CLOSED evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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; + + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb); + break; + + case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */ + /* Disconnect unless ACL collision and upper layer wants to handle it */ + if (p_ci->status != HCI_ERR_CONNECTION_EXISTS + || !btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) + { + L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, p_ci->status); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, p_ci->status); + } + break; + + case L2CEVT_L2CA_CONNECT_REQ: /* API connect request */ + /* Cancel sniff mode if needed */ +#if BTM_PWR_MGR_INCLUDED == TRUE + { + tBTM_PM_PWR_MD settings; +// btla-specific ++ + memset((void*)&settings, 0, sizeof(settings)); +// btla-specific -- + settings.mode = BTM_PM_MD_ACTIVE; +/* COVERITY +Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details] +Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details] +Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode" +// FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment. +// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored +*/ + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } +#else + BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr); +#endif + + /* If sec access does not result in started SEC_COM or COMP_NEG are already processed */ + if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + break; + + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP; + + /* Wait for the info resp in this state before sending connect req (if needed) */ + if (!p_ccb->p_lcb->w4_info_rsp) + { + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) + { + l2cu_release_ccb (p_ccb); + (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_NO_LINK); + } + else + { + l2cu_send_peer_connect_req (p_ccb); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + } + } + break; + + case L2CEVT_SEC_COMP_NEG: /* something is really bad with security */ + L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_SECURITY_BLOCK); + break; + + case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connect request */ + /* stop link timer to avoid race condition between A2MP, Security, and L2CAP */ + btu_stop_timer (&p_ccb->p_lcb->timer_entry); + + /* Cancel sniff mode if needed */ +#if BTM_PWR_MGR_INCLUDED == TRUE + { + tBTM_PM_PWR_MD settings; +// btla-specific ++ + memset((void*)&settings, 0, sizeof(settings)); +// btla-specific -- + settings.mode = BTM_PM_MD_ACTIVE; +/* COVERITY +Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details] +Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details] +Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode" +// FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment. +// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored +*/ + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } +#else + BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr); +#endif + + p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; + if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) + { + /* started the security process, tell the peer to set a longer timer */ + l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0); + } + break; + + case L2CEVT_TIMEOUT: + L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + GKI_freebuf (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_release_ccb (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_orig_w4_sec_comp +** +** Description This function handles events when the channel is in +** CST_ORIG_W4_SEC_COMP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: ORIG_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: ORIG_W4_SEC_COMP evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) + { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) + { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb); + break; + + case L2CEVT_SEC_COMP: /* Security completed success */ + /* Wait for the info resp in this state before sending connect req (if needed) */ + p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP; + if (!p_ccb->p_lcb->w4_info_rsp) + { + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) + { + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK); + } + else + { + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + l2cu_send_peer_connect_req (p_ccb); /* Start Connection */ + } + } + break; + + case L2CEVT_SEC_COMP_NEG: + L2CAP_TRACE_API2 ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, HCI_ERR_AUTH_FAILURE); + + /* If last channel immediately disconnect the ACL for better security. + Also prevents a race condition between BTM and L2CAP */ + if ( (p_ccb == p_ccb->p_lcb->ccb_queue.p_first_ccb) && (p_ccb == p_ccb->p_lcb->ccb_queue.p_last_ccb) ) + { + p_ccb->p_lcb->idle_timeout = 0; + } + + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, HCI_ERR_AUTH_FAILURE); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_freebuf (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_term_w4_sec_comp +** +** Description This function handles events when the channel is in +** CST_TERM_W4_SEC_COMP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: TERM_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: TERM_W4_SEC_COMP evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) + { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) + { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP; + + /* Wait for the info resp in next state before sending connect ind (if needed) */ + if (!p_ccb->p_lcb->w4_info_rsp) + { + /* Don't need to get info from peer or already retrieved so continue */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid, + p_ccb->p_rcb->psm, p_ccb->remote_id); + } + else + { + /* + ** L2CAP Connect Response will be sent out by 3 sec timer expiration + ** because Bluesoleil doesn't respond to L2CAP Information Request. + ** Bluesoleil seems to disconnect ACL link as failure case, because + ** it takes too long (4~7secs) to get response. + ** product version : Bluesoleil 2.1.1.0 EDR Release 060123 + ** stack version : 05.04.11.20060119 + */ + + /* Waiting for the info resp, tell the peer to set a longer timer */ + l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0); + } + break; + + case L2CEVT_SEC_COMP_NEG: + if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) + { + /* start a timer - encryption change not received before L2CAP connect req */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4); + } + else + { + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_SECURITY_BLOCK, 0); + l2cu_release_ccb (p_ccb); + } + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_freebuf (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + + /* Tell security manager to abort */ + btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr); + + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_TIMEOUT: + /* SM4 related. */ + if (!btsnd_hcic_disconnect (p_ccb->p_lcb->handle, HCI_ERR_AUTH_FAILURE)) + { + L2CAP_TRACE_API1 ("L2CAP - Calling btsnd_hcic_disconnect for handle %i failed", p_ccb->p_lcb->handle); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 1); + } + break; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm, + p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2cap_connect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CAP_CONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CAP_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CAP_CON_RSP evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + /* Send disc indication unless peer to peer race condition AND normal disconnect */ + /* *((UINT8 *)p_data) != HCI_ERR_PEER_USER happens when peer device try to disconnect for normal reason */ + p_ccb->chnl_state = CST_CLOSED; + if ((p_ccb->flags & CCB_FLAG_NO_RETRY) || !p_data || (*((UINT8 *)p_data) != HCI_ERR_PEER_USER)) + { + L2CAP_TRACE_API1 ("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); + } + p_ccb->flags |= CCB_FLAG_NO_RETRY; + break; + + case L2CEVT_L2CAP_CONNECT_RSP: /* Got peer connect confirm */ + p_ccb->remote_cid = p_ci->remote_cid; + p_ccb->chnl_state = CST_CONFIG; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Success", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_OK); + break; + + case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Got peer connect pending */ + p_ccb->remote_cid = p_ci->remote_cid; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT); + if (p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb) + { + L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Pnd_Cb(), CID: 0x%04x", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb)(p_ccb->local_cid); + } + break; + + case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer rejected connection */ + L2CAP_TRACE_API2 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Failure Code: %d", p_ccb->local_cid, p_ci->l2cap_result); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, p_ci->l2cap_result); + break; + + case L2CEVT_TIMEOUT: + L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Timeout", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* If we know peer CID from connect pending, we can send disconnect */ + if (p_ccb->remote_cid != 0) + { + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + } + else + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_freebuf (p_data); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* Need to have at least one compatible channel to continue */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) + { + l2cu_release_ccb (p_ccb); + (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK); + } + else + { + /* We have feature info, so now send peer connect request */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + l2cu_send_peer_connect_req (p_ccb); /* Start Connection */ + } + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2ca_connect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CA_CONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2C_CONN_INFO *p_ci; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CA_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CA_CON_RSP evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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; + + case L2CEVT_L2CA_CONNECT_RSP: + p_ci = (tL2C_CONN_INFO *)p_data; + + /* Result should be OK or PENDING */ + if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK)) + { + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_OK, 0); + p_ccb->chnl_state = CST_CONFIG; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + } + else + { + /* If pending, stay in same state and start extended timer */ + l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT); + } + break; + + case L2CEVT_L2CA_CONNECT_RSP_NEG: + p_ci = (tL2C_CONN_INFO *)p_data; + l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status); + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_NO_PSM, 0); + L2CAP_TRACE_API1 ("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; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_freebuf (p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* We have feature info, so now give the upper layer connect IND */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT); + L2CAP_TRACE_API1 ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid); + + (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, + p_ccb->local_cid, + p_ccb->p_rcb->psm, + p_ccb->remote_id); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_config +** +** Description This function handles events when the channel is in +** CONFIG state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CAP_CFG_INFO *p_cfg = (tL2CAP_CFG_INFO *)p_data; + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + UINT8 cfg_result; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: CONFIG evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: CONFIG evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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; + + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */ + + if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) + { + L2CAP_TRACE_EVENT2 ("L2CAP - Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d", + p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT)); + (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg); + } + else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT) + { + /* Disconnect if channels are incompatible */ + L2CAP_TRACE_EVENT0 ("L2CAP - incompatible configurations disconnect"); + l2cu_disconnect_chnl (p_ccb); + } + else /* Return error to peer so he can renegotiate if possible */ + { + L2CAP_TRACE_EVENT0 ("L2CAP - incompatible configurations trying reconfig"); + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + } + break; + + case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */ + l2cu_process_peer_cfg_rsp (p_ccb, p_cfg); + + if (p_cfg->result != L2CAP_CFG_PENDING) + { + /* TBD: When config options grow beyong minimum MTU (48 bytes) + * logic needs to be added to handle responses with + * continuation bit set in flags field. + * 1. Send additional config request out until C-bit is cleared in response + */ + p_ccb->config_done |= OB_CFG_DONE; + + if (p_ccb->config_done & IB_CFG_DONE) + { + /* Verify two sides are in compatible modes before continuing */ + if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) + { + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_WARNING1 ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + } + + p_ccb->config_done |= RECONFIG_FLAG; + p_ccb->chnl_state = CST_OPEN; + l2c_link_adjust_chnl_allocation (); + btu_stop_timer (&p_ccb->timer_entry); + + /* If using eRTM and waiting for an ACK, restart the ACK timer */ + if (p_ccb->fcrb.wait_ack) + l2c_fcr_start_timer(p_ccb); + + /* + ** check p_ccb->our_cfg.fcr.mon_tout and p_ccb->our_cfg.fcr.rtrans_tout + ** we may set them to zero when sending config request during renegotiation + */ + if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + &&((p_ccb->our_cfg.fcr.mon_tout == 0)||(p_ccb->our_cfg.fcr.rtrans_tout))) + { + l2c_fcr_adj_monitor_retran_timeout (p_ccb); + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.connect_tick_count = GKI_get_os_tick_count(); +#endif + /* See if we can forward anything on the hold queue */ + if (p_ccb->xmit_hold_q.count) + { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + } + } + + L2CAP_TRACE_API1 ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg); + break; + + case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer config error rsp */ + /* Disable the Timer */ + btu_stop_timer (&p_ccb->timer_entry); + + /* If failure was channel mode try to renegotiate */ + if (l2c_fcr_renegotiate_chan(p_ccb, p_cfg) == FALSE) + { + L2CAP_TRACE_API2 ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x, Failure: %d", p_ccb->local_cid, p_cfg->result); + (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; + L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE); + break; + + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */ + l2cu_process_our_cfg_req (p_ccb, p_cfg); + l2cu_send_peer_config_req (p_ccb, p_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */ + l2cu_process_our_cfg_rsp (p_ccb, p_cfg); + + /* Not finished if continuation flag is set */ + if ( (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT) || (p_cfg->result == L2CAP_CFG_PENDING) ) + { + /* Send intermediate response; remain in cfg state */ + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + break; + } + + /* Local config done; clear cached configuration in case reconfig takes place later */ + p_ccb->peer_cfg.mtu_present = FALSE; + p_ccb->peer_cfg.flush_to_present = FALSE; + p_ccb->peer_cfg.qos_present = FALSE; + + p_ccb->config_done |= IB_CFG_DONE; + + if (p_ccb->config_done & OB_CFG_DONE) + { + /* Verify two sides are in compatible modes before continuing */ + if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) + { + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_WARNING1 ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid); + l2cu_release_ccb (p_ccb); + (*disconnect_ind)(local_cid, FALSE); + break; + } + + p_ccb->config_done |= RECONFIG_FLAG; + p_ccb->chnl_state = CST_OPEN; + l2c_link_adjust_chnl_allocation (); + btu_stop_timer (&p_ccb->timer_entry); + } + + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + + /* If using eRTM and waiting for an ACK, restart the ACK timer */ + if (p_ccb->fcrb.wait_ack) + l2c_fcr_start_timer(p_ccb); + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.connect_tick_count = GKI_get_os_tick_count(); +#endif + + /* See if we can forward anything on the hold queue */ + if ( (p_ccb->chnl_state == CST_OPEN) && (p_ccb->xmit_hold_q.count) ) + { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } + break; + + case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config reject */ + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + L2CAP_TRACE_API1 ("L2CAP - Calling DataInd_Cb(), CID: 0x%04x", p_ccb->local_cid); +#if (L2CAP_NUM_FIXED_CHNLS > 0) + 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); + else + GKI_freebuf (p_data); + + break; + } +#endif + (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + if (p_ccb->config_done & OB_CFG_DONE) + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data); + else + GKI_freebuf (p_data); + break; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_disc_req (p_ccb); + L2CAP_TRACE_API1 ("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; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_open +** +** Description This function handles events when the channel is in +** OPEN state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + UINT16 local_cid = p_ccb->local_cid; + tL2CAP_CFG_INFO *p_cfg; + tL2C_CHNL_STATE tempstate; + UINT8 tempcfgdone; + UINT8 cfg_result; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event); +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( local_cid == L2CAP_CONNECTIONLESS_CID ) + { + /* check if this event can be processed by UCD */ + if ( l2c_ucd_process_event (p_ccb, event, p_data) ) + { + /* The event is processed by UCD state machine */ + return; + } + } +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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); + break; + + case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */ + /* Tell upper layer. If service guaranteed, then clear the channel */ + if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) + (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr); + break; + + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */ + p_cfg = (tL2CAP_CFG_INFO *)p_data; + + tempstate = p_ccb->chnl_state; + tempcfgdone = p_ccb->config_done; + p_ccb->chnl_state = CST_CONFIG; + p_ccb->config_done &= ~CFG_DONE_MASK; + + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + + if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) + { + (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg); + } + + /* Error in config parameters: reset state and config flag */ + else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE) + { + btu_stop_timer(&p_ccb->timer_entry); + p_ccb->chnl_state = tempstate; + p_ccb->config_done = tempcfgdone; + l2cu_send_peer_config_rsp (p_ccb, p_cfg); + } + else /* L2CAP_PEER_CFG_DISCONNECT */ + { + /* Disconnect if channels are incompatible + * Note this should not occur if reconfigure + * since this should have never passed original config. + */ + l2cu_disconnect_chnl (p_ccb); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */ +// btla-specific ++ + /* Make sure we are not in sniff mode */ +#if BTM_PWR_MGR_INCLUDED == TRUE + { + tBTM_PM_PWR_MD settings; + settings.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } +#else + BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr); +#endif +// btla-specific -- + + p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid); + (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data); + break; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */ + /* Make sure we are not in sniff mode */ +#if BTM_PWR_MGR_INCLUDED == TRUE + { + tBTM_PM_PWR_MD settings; + settings.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings); + } +#else + BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr); +#endif + + l2cu_send_peer_disc_req (p_ccb); + p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP; + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data); + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + break; + + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */ + p_ccb->chnl_state = CST_CONFIG; + p_ccb->config_done &= ~CFG_DONE_MASK; + l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data); + l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + break; + + case L2CEVT_TIMEOUT: + /* Process the monitor/retransmission time-outs in flow control/retrans mode */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + l2c_fcr_proc_tout (p_ccb); + break; + + case L2CEVT_ACK_TIMEOUT: + l2c_fcr_proc_ack_tout (p_ccb); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2cap_disconnect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CAP_DISCONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_CFM_CB *disconnect_cfm = p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CAP_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CAP_DISC_RSP evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */ + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) + { + L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_OK); + } + break; + + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) + { + L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_OK); + } + break; + + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + case L2CEVT_TIMEOUT: /* Timeout */ + l2cu_release_ccb (p_ccb); + if (disconnect_cfm) + { + L2CAP_TRACE_API1 ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid); + (*disconnect_cfm)(local_cid, L2CAP_DISC_TIMEOUT); + } + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + GKI_freebuf (p_data); + break; + } +} + + +/******************************************************************************* +** +** Function l2c_csm_w4_l2ca_disconnect_rsp +** +** Description This function handles events when the channel is in +** CST_W4_L2CA_DISCONNECT_RSP state. +** +** Returns void +** +*******************************************************************************/ +static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + UINT16 local_cid = p_ccb->local_cid; + +#if (BT_TRACE_VERBOSE == TRUE) + L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: W4_L2CA_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event)); +#else + L2CAP_TRACE_EVENT1 ("L2CAP - st: W4_L2CA_DISC_RSP evt: %d", event); +#endif + + switch (event) + { + case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */ + L2CAP_TRACE_API1 ("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; + + case L2CEVT_TIMEOUT: + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + L2CAP_TRACE_API1 ("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; + + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper disconnect request */ + case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper disconnect response */ + l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid); + l2cu_release_ccb (p_ccb); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + GKI_freebuf (p_data); + break; + } +} + + +#if (BT_TRACE_VERBOSE == TRUE) +/******************************************************************************* +** +** Function l2c_csm_get_event_name +** +** Description This function returns the event name. +** +** NOTE conditionally compiled to save memory. +** +** Returns pointer to the name +** +*******************************************************************************/ +static char *l2c_csm_get_event_name (UINT16 event) +{ + switch (event) + { + case L2CEVT_LP_CONNECT_CFM: /* Lower layer connect confirm */ + return ("LOWER_LAYER_CONNECT_CFM"); + case L2CEVT_LP_CONNECT_CFM_NEG: /* Lower layer connect confirm (failed) */ + return ("LOWER_LAYER_CONNECT_CFM_NEG"); + case L2CEVT_LP_CONNECT_IND: /* Lower layer connect indication */ + return ("LOWER_LAYER_CONNECT_IND"); + case L2CEVT_LP_DISCONNECT_IND: /* Lower layer disconnect indication */ + return ("LOWER_LAYER_DISCONNECT_IND"); + case L2CEVT_LP_QOS_CFM: /* Lower layer QOS confirmation */ + return ("LOWER_LAYER_QOS_CFM"); + case L2CEVT_LP_QOS_CFM_NEG: /* Lower layer QOS confirmation (failed)*/ + return ("LOWER_LAYER_QOS_CFM_NEG"); + case L2CEVT_LP_QOS_VIOLATION_IND: /* Lower layer QOS violation indication */ + return ("LOWER_LAYER_QOS_VIOLATION_IND"); + + case L2CEVT_SEC_COMP: /* Security cleared successfully */ + return ("SECURITY_COMPLETE"); + case L2CEVT_SEC_COMP_NEG: /* Security procedure failed */ + return ("SECURITY_COMPLETE_NEG"); + + case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connection request */ + return ("PEER_CONNECT_REQ"); + case L2CEVT_L2CAP_CONNECT_RSP: /* Peer connection response */ + return ("PEER_CONNECT_RSP"); + case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Peer connection response pending */ + return ("PEER_CONNECT_RSP_PND"); + case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer connection response (failed) */ + return ("PEER_CONNECT_RSP_NEG"); + case L2CEVT_L2CAP_CONFIG_REQ: /* Peer configuration request */ + return ("PEER_CONFIG_REQ"); + case L2CEVT_L2CAP_CONFIG_RSP: /* Peer configuration response */ + return ("PEER_CONFIG_RSP"); + case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer configuration response (failed) */ + return ("PEER_CONFIG_RSP_NEG"); + case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */ + return ("PEER_DISCONNECT_REQ"); + case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */ + return ("PEER_DISCONNECT_RSP"); + case L2CEVT_L2CAP_DATA: /* Peer data */ + return ("PEER_DATA"); + + case L2CEVT_L2CA_CONNECT_REQ: /* Upper layer connect request */ + return ("UPPER_LAYER_CONNECT_REQ"); + case L2CEVT_L2CA_CONNECT_RSP: /* Upper layer connect response */ + return ("UPPER_LAYER_CONNECT_RSP"); + case L2CEVT_L2CA_CONNECT_RSP_NEG: /* Upper layer connect response (failed)*/ + return ("UPPER_LAYER_CONNECT_RSP_NEG"); + case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config request */ + return ("UPPER_LAYER_CONFIG_REQ"); + case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config response */ + return ("UPPER_LAYER_CONFIG_RSP"); + case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config response (failed) */ + return ("UPPER_LAYER_CONFIG_RSP_NEG"); + case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper layer disconnect request */ + return ("UPPER_LAYER_DISCONNECT_REQ"); + case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper layer disconnect response */ + return ("UPPER_LAYER_DISCONNECT_RSP"); + case L2CEVT_L2CA_DATA_READ: /* Upper layer data read */ + return ("UPPER_LAYER_DATA_READ"); + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data write */ + return ("UPPER_LAYER_DATA_WRITE"); + case L2CEVT_TIMEOUT: /* Timeout */ + return ("TIMEOUT"); + case L2CEVT_SEC_RE_SEND_CMD: + return ("SEC_RE_SEND_CMD"); + case L2CEVT_L2CAP_INFO_RSP: /* Peer information response */ + return ("L2CEVT_L2CAP_INFO_RSP"); + case L2CEVT_ACK_TIMEOUT: + return ("L2CEVT_ACK_TIMEOUT"); + + default: + return ("???? UNKNOWN EVENT"); + } +} +#endif /* (BT_TRACE_VERBOSE == TRUE) */ + + +/******************************************************************************* +** +** Function l2c_enqueue_peer_data +** +** Description Enqueues data destined for the peer in the ccb. Handles +** FCR segmentation and checks for congestion. +** +** Returns void +** +*******************************************************************************/ +void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT8 *p; + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + p_buf->event = 0; + } + else + { + /* Save the channel ID for faster counting */ + p_buf->event = p_ccb->local_cid; + + /* Step back to add the L2CAP header */ + p_buf->offset -= L2CAP_PKT_OVERHEAD; + p_buf->len += L2CAP_PKT_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* Now the L2CAP header */ + UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + } + + GKI_enqueue (&p_ccb->xmit_hold_q, p_buf); + + l2cu_check_channel_congestion (p_ccb); + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* if new packet is higher priority than serving ccb and it is not overrun */ + if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority ) + &&( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0)) + { + /* send out higher priority packet */ + p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority; + } +#endif + + /* if we are doing a round robin scheduling, set the flag */ + if (p_ccb->p_lcb->link_xmit_quota == 0) + l2cb.check_round_robin = TRUE; +} + + diff --git a/stack/l2cap/l2c_fcr.c b/stack/l2cap/l2c_fcr.c new file mode 100644 index 0000000..ff018c5 --- /dev/null +++ b/stack/l2cap/l2c_fcr.c @@ -0,0 +1,2709 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP 1.2 Flow Control and retransmissions + * functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "l2c_api.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" + + +/* Flag passed to retransmit_i_frames() when all packets should be retransmitted */ +#define L2C_FCR_RETX_ALL_PKTS 0xFF + +#if BT_TRACE_VERBOSE == TRUE +static char *SAR_types[] = { "Unsegmented", "Start", "End", "Continuation" }; +static char *SUP_types[] = { "RR", "REJ", "RNR", "SREJ" }; +#endif + +/* Look-up table for the CRC calculation */ +static const unsigned short crctab[256] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, +}; + + +/******************************************************************************* +** Static local functions +*/ +static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word); +static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word); +static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack); +static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq); +static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission); +static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf); +static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word); + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE +static BOOLEAN l2c_corrupt_the_fcr_packet (tL2C_CCB *p_ccb, BT_HDR *p_buf, + BOOLEAN is_rx, UINT16 ctrl_word); +static BOOLEAN l2c_bypass_sframe_packet (tL2C_CCB *p_ccb); +#endif + +#if (L2CAP_ERTM_STATS == TRUE) +static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked); +#endif + +/******************************************************************************* +** +** Function l2c_fcr_updcrc +** +** Description This function computes the CRC using the look-up table. +** +** Returns CRC +** +*******************************************************************************/ +unsigned short l2c_fcr_updcrc(unsigned short icrc, unsigned char *icp, int icnt) +{ + register unsigned short crc = icrc; + register unsigned char *cp = icp; + register int cnt = icnt; + + while (cnt--) + { + crc = ((crc >> 8) & 0xff) ^ crctab[(crc & 0xff) ^ *cp++]; + } + + return(crc); +} + + +/******************************************************************************* +** +** Function l2c_fcr_tx_get_fcs +** +** Description This function computes the CRC for a frame to be TXed. +** +** Returns CRC +** +*******************************************************************************/ +UINT16 l2c_fcr_tx_get_fcs (BT_HDR *p_buf) +{ + UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + + return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len)); +} + +/******************************************************************************* +** +** Function l2c_fcr_rx_get_fcs +** +** Description This function computes the CRC for a received frame. +** +** Returns CRC +** +*******************************************************************************/ +UINT16 l2c_fcr_rx_get_fcs (BT_HDR *p_buf) +{ + UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + + /* offset points past the L2CAP header, but the CRC check includes it */ + p -= L2CAP_PKT_OVERHEAD; + + return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len + L2CAP_PKT_OVERHEAD)); +} + +/******************************************************************************* +** +** Function l2c_fcr_start_timer +** +** Description This function starts the (monitor or retransmission) timer. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_start_timer (tL2C_CCB *p_ccb) +{ + UINT32 tout; + + /* The timers which are in milliseconds */ + if (p_ccb->fcrb.wait_ack) + { + tout = (UINT32)p_ccb->our_cfg.fcr.mon_tout; + } + else + { + tout = (UINT32)p_ccb->our_cfg.fcr.rtrans_tout; + } +/* + L2CAP_TRACE_DEBUG4 ("l2c_fcr_start_timer Tout: %u Already Running: %u wait_ack: %u ack_q_count: %u", + tout, p_ccb->timer_entry.in_use, p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count); +*/ + /* Only start a timer that was not started */ + if (p_ccb->fcrb.mon_retrans_timer.in_use == 0) + btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, tout*QUICK_TIMER_TICKS_PER_SEC/1000); +} + +/******************************************************************************* +** +** Function l2c_fcr_stop_timer +** +** Description This function stops the (monitor or transmission) timer. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_stop_timer (tL2C_CCB *p_ccb) +{ + if (p_ccb->fcrb.mon_retrans_timer.in_use) + { +/* + L2CAP_TRACE_DEBUG2 ("l2c_fcr_stop_timer wait_ack: %u ack_q_count: %u", + p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count); +*/ + btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_cleanup +** +** Description This function cleans up the variable used for flow-control/retrans. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_cleanup (tL2C_CCB *p_ccb) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + + l2c_fcr_stop_timer (p_ccb); + + if (p_fcrb->p_rx_sdu) + GKI_freebuf (p_fcrb->p_rx_sdu); + + while (p_fcrb->waiting_for_ack_q.p_first) + GKI_freebuf (GKI_dequeue (&p_fcrb->waiting_for_ack_q)); + + while (p_fcrb->srej_rcv_hold_q.p_first) + GKI_freebuf (GKI_dequeue (&p_fcrb->srej_rcv_hold_q)); + + while (p_fcrb->retrans_q.p_first) + GKI_freebuf (GKI_dequeue (&p_fcrb->retrans_q)); + + btu_stop_quick_timer (&p_fcrb->ack_timer); + btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer); + +#if (L2CAP_ERTM_STATS == TRUE) + if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) ) + { + UINT32 dur = GKI_get_os_tick_count() - p_ccb->fcrb.connect_tick_count; + char *p_str = (char *)GKI_getbuf(120); + UINT16 i; + UINT32 throughput_avg, ack_delay_avg, ack_q_count_avg; + + dur = GKI_OS_TICKS_TO_MS(dur); + BT_TRACE_2(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "--- L2CAP ERTM Stats for CID: 0x%04x Duration: %08ums", p_ccb->local_cid, dur); + BT_TRACE_4(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "Retransmissions:%08u Times Flow Controlled:%08u Retrans Touts:%08u Ack Touts:%08u", + p_ccb->fcrb.pkts_retransmitted, p_ccb->fcrb.xmit_window_closed, p_ccb->fcrb.retrans_touts, p_ccb->fcrb.xmit_ack_touts); + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "Times there is less than 2 packets in controller when flow controlled:%08u", p_ccb->fcrb.controller_idle); + BT_TRACE_2(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "max_held_acks:%08u, in_cfg.fcr.tx_win_sz:%08u", p_ccb->fcrb.max_held_acks, p_ccb->peer_cfg.fcr.tx_win_sz ); + if (p_str) + { + sprintf(p_str, "Sent Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u", + p_ccb->fcrb.ertm_pkt_counts[0], p_ccb->fcrb.ertm_byte_counts[0], + (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[0] * 100) / (dur / 10) : 0), + p_ccb->fcrb.s_frames_sent[0], p_ccb->fcrb.s_frames_sent[1], p_ccb->fcrb.s_frames_sent[2], p_ccb->fcrb.s_frames_sent[3]); + + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + sprintf(p_str, "Rcvd Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u", + p_ccb->fcrb.ertm_pkt_counts[1], p_ccb->fcrb.ertm_byte_counts[1], + (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[1] * 100) / (dur / 10) : 0), + p_ccb->fcrb.s_frames_rcvd[0], p_ccb->fcrb.s_frames_rcvd[1], p_ccb->fcrb.s_frames_rcvd[2], p_ccb->fcrb.s_frames_rcvd[3]); + + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + throughput_avg = 0; + ack_delay_avg = 0; + ack_q_count_avg = 0; + + for (i = 0; i < L2CAP_ERTM_STATS_NUM_AVG; i++ ) + { + if (i == p_ccb->fcrb.ack_delay_avg_index ) + { + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "[%02u] collecting data ...", i ); + continue; + } + + sprintf(p_str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u", + i, p_ccb->fcrb.throughput[i], + p_ccb->fcrb.ack_delay_avg[i], p_ccb->fcrb.ack_delay_min[i], p_ccb->fcrb.ack_delay_max[i], + p_ccb->fcrb.ack_q_count_avg[i], p_ccb->fcrb.ack_q_count_min[i], p_ccb->fcrb.ack_q_count_max[i] ); + + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str); + + throughput_avg += p_ccb->fcrb.throughput[i]; + ack_delay_avg += p_ccb->fcrb.ack_delay_avg[i]; + ack_q_count_avg += p_ccb->fcrb.ack_q_count_avg[i]; + } + + throughput_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + ack_delay_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + ack_q_count_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1); + + BT_TRACE_3(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "throughput_avg: %8u (kbytes/sec), ack_delay_avg: %8u ms, ack_q_count_avg: %8u", + throughput_avg, ack_delay_avg, ack_q_count_avg ); + + GKI_freebuf(p_str); + } + + BT_TRACE_0(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, + "---"); + } +#endif + + memset (p_fcrb, 0, sizeof (tL2C_FCRB)); +} + +/******************************************************************************* +** +** Function l2c_fcr_clone_buf +** +** Description This function allocates and copies requested part of a buffer +** at a new-offset. +** +** Returns pointer to new buffer +** +*******************************************************************************/ +BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes, UINT8 pool) +{ + BT_HDR *p_buf2; + + /* If using the common pool, should be at least 10% free. */ + if ( (pool == HCI_ACL_POOL_ID) && (GKI_poolutilization (pool) > 90) ) + { + L2CAP_TRACE_ERROR1 ("L2CAP - failed to clone buffer on HCI_ACL_POOL_ID Utilization: %u", GKI_poolutilization(pool)); + return (NULL); + } + + if ((p_buf2 = (BT_HDR *)GKI_getpoolbuf(pool)) != NULL) + { + UINT16 pool_buf_size = GKI_get_pool_bufsize (pool); + + /* Make sure buffer fits into buffer pool */ + if ((no_of_bytes + sizeof(BT_HDR) + new_offset) > pool_buf_size) + { + L2CAP_TRACE_ERROR5("##### l2c_fcr_clone_buf (NumBytes %d) -> Exceeds poolsize %d [bytes %d + BT_HDR %d + offset %d]", + (no_of_bytes + sizeof(BT_HDR) + new_offset), + pool_buf_size, no_of_bytes, sizeof(BT_HDR), + new_offset); + + GKI_freebuf(p_buf2); + return (NULL); + } + + p_buf2->offset = new_offset; + p_buf2->len = no_of_bytes; + + memcpy (((UINT8 *)(p_buf2 + 1)) + p_buf2->offset, + ((UINT8 *)(p_buf + 1)) + p_buf->offset, + no_of_bytes); + } + else + { + L2CAP_TRACE_ERROR2 ("L2CAP - failed to clone buffer, Pool: %u Count: %u", pool, GKI_poolfreecount(pool)); + } + + return (p_buf2); +} + +/******************************************************************************* +** +** Function l2c_fcr_is_flow_controlled +** +** Description This function checks if the CCB is flow controlled by peer. +** +** Returns The control word +** +*******************************************************************************/ +BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb) +{ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + { + /* Check if remote side flowed us off or the transmit window is full */ + if ( (p_ccb->fcrb.remote_busy == TRUE) + || (p_ccb->fcrb.waiting_for_ack_q.count >= p_ccb->peer_cfg.fcr.tx_win_sz) ) + { +#if (L2CAP_ERTM_STATS == TRUE) + if (p_ccb->xmit_hold_q.count != 0) + { + p_ccb->fcrb.xmit_window_closed++; + + if ((p_ccb->p_lcb->sent_not_acked < 2)&&(l2cb.controller_xmit_window > 0)) + p_ccb->fcrb.controller_idle++; + } +#endif + return (TRUE); + } + } + return (FALSE); +} + +/******************************************************************************* +** +** Function prepare_I_frame +** +** Description This function sets the FCR variables in an I-frame that is +** about to be sent to HCI for transmission. This may be the +** first time the I-frame is sent, or a retransmission +** +** Returns - +** +*******************************************************************************/ +static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 *p; + UINT16 fcs; + UINT16 ctrl_word; + BOOLEAN set_f_bit = p_fcrb->send_f_rsp; + + p_fcrb->send_f_rsp = FALSE; + + if (is_retransmission) + { + /* Get the old control word and clear out the old req_seq and F bits */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + ctrl_word &= ~(L2CAP_FCR_REQ_SEQ_BITS + L2CAP_FCR_F_BIT); + } + else + { + ctrl_word = p_buf->layer_specific & L2CAP_FCR_SEG_BITS; /* SAR bits */ + ctrl_word |= (p_fcrb->next_tx_seq << L2CAP_FCR_TX_SEQ_BITS_SHIFT); /* Tx Seq */ + + p_fcrb->next_tx_seq = (p_fcrb->next_tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + } + + /* Set the F-bit and reqseq only if using re-transmission mode */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + { + if (set_f_bit) + ctrl_word |= L2CAP_FCR_F_BIT; + + ctrl_word |= (p_fcrb->next_seq_expected) << L2CAP_FCR_REQ_SEQ_BITS_SHIFT; + + p_fcrb->last_ack_sent = p_ccb->fcrb.next_seq_expected; + + if (p_ccb->fcrb.ack_timer.in_use) + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + + /* Set the control word */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + UINT16_TO_STREAM (p, ctrl_word); + + /* Compute the FCS and add to the end of the buffer if not bypassed */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + { + /* length field in l2cap header has to include FCS length */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset; + UINT16_TO_STREAM (p, p_buf->len + L2CAP_FCS_LEN - L2CAP_PKT_OVERHEAD); + + /* Calculate the FCS */ + fcs = l2c_fcr_tx_get_fcs(p_buf); + + /* Point to the end of the buffer and put the FCS there */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len; + + UINT16_TO_STREAM (p, fcs); + + p_buf->len += L2CAP_FCS_LEN; + } + +#if BT_TRACE_VERBOSE == TRUE + if (is_retransmission) + { + L2CAP_TRACE_EVENT6 ("L2CAP eRTM ReTx I-frame CID: 0x%04x Len: %u SAR: %s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + else + { + L2CAP_TRACE_EVENT6 ("L2CAP eRTM Tx I-frame CID: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } +#endif + + /* Start the retransmission timer if not already running */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + l2c_fcr_start_timer (p_ccb); +} + +/******************************************************************************* +** +** Function l2c_fcr_send_S_frame +** +** Description This function formats and sends an S-frame for transmission. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT16 ctrl_word; + UINT16 fcs; + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + /* Only used for conformance testing */ + if (l2c_bypass_sframe_packet (p_ccb)) + return; +#endif + + if ((!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN)) + return; + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.s_frames_sent[function_code]++; +#endif + + if (pf_bit == L2CAP_FCR_P_BIT) + { + p_ccb->fcrb.wait_ack = TRUE; + + l2c_fcr_stop_timer (p_ccb); /* Restart the monitor timer */ + l2c_fcr_start_timer (p_ccb); + } + + /* Create the control word to use */ + ctrl_word = (function_code << L2CAP_FCR_SUP_SHIFT) | L2CAP_FCR_S_FRAME_BIT; + ctrl_word |= (p_ccb->fcrb.next_seq_expected << L2CAP_FCR_REQ_SEQ_BITS_SHIFT); + ctrl_word |= pf_bit; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID)) != NULL) + { + p_buf->offset = HCI_DATA_PREAMBLE_SIZE; + p_buf->len = L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* Put in the L2CAP header */ + UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD + L2CAP_FCS_LEN); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, ctrl_word); + + /* Compute the FCS and add to the end of the buffer if not bypassed */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + { + fcs = l2c_fcr_tx_get_fcs (p_buf); + + UINT16_TO_STREAM (p, fcs); + p_buf->len += L2CAP_FCS_LEN; + } + else /* rewrite the length without FCS length */ + { + p -= 6; + UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD); + } + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + /* Get out if packet was dropped */ + if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, FALSE, ctrl_word)) + return; +#endif + /* Now, the HCI transport header */ + p_buf->layer_specific = L2CAP_NON_FLUSHABLE_PKT; + l2cu_set_acl_hci_header (p_buf, p_ccb); + +#if BT_TRACE_VERBOSE == TRUE + if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1) + || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) + { + L2CAP_TRACE_WARNING6 ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, ctrl_word, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + L2CAP_TRACE_WARNING1 (" Buf Len: %u", p_buf->len); + } + else + { + L2CAP_TRACE_EVENT6 ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, ctrl_word, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + L2CAP_TRACE_EVENT1 (" Buf Len: %u", p_buf->len); + } +#endif /* BT_TRACE_VERBOSE */ + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); + + p_ccb->fcrb.last_ack_sent = p_ccb->fcrb.next_seq_expected; + + if (p_ccb->fcrb.ack_timer.in_use) + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + else + { + L2CAP_TRACE_ERROR2 ("l2c_fcr_send_S_frame(No Resources) cid 0x%04x, Type: 0x%4x", + p_ccb->local_cid, function_code); + } +} + + +/******************************************************************************* +** +** Function l2c_fcr_proc_pdu +** +** Description This function is the entry point for processing of a +** received PDU when in flow control and/or retransmission modes. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT16 fcs; + UINT16 min_pdu_len; + UINT16 ctrl_word; + + /* Check the length */ + min_pdu_len = (p_ccb->bypass_fcs == L2CAP_BYPASS_FCS) ? + (UINT16)L2CAP_FCR_OVERHEAD : (UINT16)(L2CAP_FCS_LEN + L2CAP_FCR_OVERHEAD); + + if (p_buf->len < min_pdu_len) + { + L2CAP_TRACE_WARNING2 ("Rx L2CAP PDU: CID: 0x%04x Len too short: %u", p_ccb->local_cid, p_buf->len); + GKI_freebuf (p_buf); + return; + } + + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_STREAM_MODE) + { + process_stream_frame (p_ccb, p_buf); + return; + } + +#if BT_TRACE_VERBOSE == TRUE + /* Get the control word */ + p = ((UINT8 *)(p_buf+1)) + p_buf->offset; + STREAM_TO_UINT16 (ctrl_word, p); + + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + { + if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1) + || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) + { + /* REJ or SREJ */ + L2CAP_TRACE_WARNING6 ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, p_buf->len, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + else + { + L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u", + p_ccb->local_cid, p_buf->len, + SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT], + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + } + else + { + L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); + } + + L2CAP_TRACE_EVENT6 (" eRTM Rx Nxt_tx_seq %u, Lst_rx_ack %u, Nxt_seq_exp %u, Lst_ack_snt %u, wt_q.cnt %u, tries %u", + p_ccb->fcrb.next_tx_seq, p_ccb->fcrb.last_rx_ack, p_ccb->fcrb.next_seq_expected, + p_ccb->fcrb.last_ack_sent, p_ccb->fcrb.waiting_for_ack_q.count, p_ccb->fcrb.num_tries); + +#endif /* BT_TRACE_VERBOSE */ + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + p = ((UINT8 *)(p_buf+1)) + p_buf->offset; + STREAM_TO_UINT16 (ctrl_word, p); + + /* Get out if packet was dropped */ + if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, TRUE, ctrl_word)) + return; +#endif /* L2CAP_CORRUPT_ERTM_PKTS */ + + /* Verify FCS if using */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + { + p = ((UINT8 *)(p_buf+1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN; + + /* Extract and drop the FCS from the packet */ + STREAM_TO_UINT16 (fcs, p); + p_buf->len -= L2CAP_FCS_LEN; + + if (l2c_fcr_rx_get_fcs(p_buf) != fcs) + { + L2CAP_TRACE_WARNING1 ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid); + GKI_freebuf(p_buf); + return; + } + } + + /* Get the control word */ + p = ((UINT8 *)(p_buf+1)) + p_buf->offset; + + STREAM_TO_UINT16 (ctrl_word, p); + + p_buf->len -= L2CAP_FCR_OVERHEAD; + p_buf->offset += L2CAP_FCR_OVERHEAD; + + /* If we had a poll bit outstanding, check if we got a final response */ + if (p_ccb->fcrb.wait_ack) + { + /* If final bit not set, ignore the frame unless it is a polled S-frame */ + if ( !(ctrl_word & L2CAP_FCR_F_BIT) ) + { + if ( (ctrl_word & L2CAP_FCR_P_BIT) && (ctrl_word & L2CAP_FCR_S_FRAME_BIT) ) + { + if (p_ccb->fcrb.srej_sent) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT); + else if (p_ccb->fcrb.local_busy) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT); + + /* Got a poll while in wait_ack state, so re-start our timer with 1-second */ + /* This is a small optimization... the monitor timer is 12 secs, but we saw */ + /* that if the other side sends us a poll when we are waiting for a final, */ + /* then it speeds up recovery significantly if we poll him back soon after his poll. */ + btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, QUICK_TIMER_TICKS_PER_SEC); + } + GKI_freebuf (p_buf); + return; + } + + p_ccb->fcrb.wait_ack = FALSE; + + /* P and F are mutually exclusive */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + ctrl_word &= ~L2CAP_FCR_P_BIT; + + if (p_ccb->fcrb.waiting_for_ack_q.count == 0) + p_ccb->fcrb.num_tries = 0; + + l2c_fcr_stop_timer (p_ccb); + } + else + { + /* Otherwise, ensure the final bit is ignored */ + ctrl_word &= ~L2CAP_FCR_F_BIT; + } + + /* Process receive sequence number */ + if (!process_reqseq (p_ccb, ctrl_word)) + { + GKI_freebuf (p_buf); + return; + } + + /* Process based on whether it is an S-frame or an I-frame */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + process_s_frame (p_ccb, p_buf, ctrl_word); + else + process_i_frame (p_ccb, p_buf, ctrl_word, FALSE); + + /* Return if the channel got disconnected by a bad packet or max retransmissions */ + if ( (!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN) ) + return; + + /* If we have some buffers held while doing SREJ, and SREJ has cleared, process them now */ + if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.srej_sent) && (p_ccb->fcrb.srej_rcv_hold_q.count > 0) ) + { + BUFFER_Q temp_q = p_ccb->fcrb.srej_rcv_hold_q; + + GKI_init_q (&p_ccb->fcrb.srej_rcv_hold_q); + + while ((p_buf = (BT_HDR *)GKI_dequeue (&temp_q)) != NULL) + { + if (p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) + { + /* Get the control word */ + p = ((UINT8 *)(p_buf+1)) + p_buf->offset - L2CAP_FCR_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + L2CAP_TRACE_DEBUG3 ("l2c_fcr_proc_pdu() CID: 0x%04x Process Buffer from SREJ_Hold_Q TxSeq: %u Expected_Seq: %u", + p_ccb->local_cid, (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + p_ccb->fcrb.next_seq_expected); + + /* Process the SREJ held I-frame, but do not send an RR for each individual frame */ + process_i_frame (p_ccb, p_buf, ctrl_word, TRUE); + } + else + GKI_freebuf (p_buf); + + /* If more frames were lost during SREJ, send a REJ */ + if (p_ccb->fcrb.rej_after_srej) + { + p_ccb->fcrb.rej_after_srej = FALSE; + p_ccb->fcrb.rej_sent = TRUE; + + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0); + } + } + + /* Now, if needed, send one RR for the whole held queue */ + if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.rej_sent) && (!p_ccb->fcrb.srej_sent) + && (p_ccb->fcrb.next_seq_expected != p_ccb->fcrb.last_ack_sent) ) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + else + { + L2CAP_TRACE_DEBUG6 ("l2c_fcr_proc_pdu() not sending RR CID: 0x%04x local_busy:%d rej_sent:%d srej_sent:%d Expected_Seq:%u Last_Ack:%u", + p_ccb->local_cid, p_ccb->fcrb.local_busy, p_ccb->fcrb.rej_sent, p_ccb->fcrb.srej_sent, p_ccb->fcrb.next_seq_expected, + p_ccb->fcrb.last_ack_sent); + } + } + + /* If a window has opened, check if we can send any more packets */ + if ( (p_ccb->fcrb.retrans_q.count || p_ccb->xmit_hold_q.count) + && (p_ccb->fcrb.wait_ack == FALSE) + && (l2c_fcr_is_flow_controlled (p_ccb) == FALSE) ) + { + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } +} + +/******************************************************************************* +** +** Function l2c_fcr_proc_tout +** +** Description Handle a timeout. We should be in error recovery state. +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_tout (tL2C_CCB *p_ccb) +{ + L2CAP_TRACE_DEBUG5 ("l2c_fcr_proc_tout: CID: 0x%04x num_tries: %u (max: %u) wait_ack: %u ack_q_count: %u", + p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit, + p_ccb->fcrb.wait_ack, p_ccb->fcrb.waiting_for_ack_q.count); + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.retrans_touts++; +#endif + + if ( (p_ccb->peer_cfg.fcr.max_transmit != 0) && (++p_ccb->fcrb.num_tries > p_ccb->peer_cfg.fcr.max_transmit) ) + { + l2cu_disconnect_chnl (p_ccb); + } + else + { + if (!p_ccb->fcrb.srej_sent && !p_ccb->fcrb.rej_sent) + { + if (p_ccb->fcrb.local_busy) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_P_BIT); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT); + } + } +} + + +/******************************************************************************* +** +** Function l2c_fcr_proc_ack_tout +** +** Description Send RR/RNR if we have not acked I frame +** +** Returns - +** +*******************************************************************************/ +void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb) +{ + L2CAP_TRACE_DEBUG5 ("l2c_fcr_proc_ack_tout: CID: 0x%04x State: %u Wack:%u Rq:%d Acked:%d", p_ccb->local_cid, + p_ccb->chnl_state, p_ccb->fcrb.wait_ack, p_ccb->fcrb.next_seq_expected, p_ccb->fcrb.last_ack_sent); + + if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) + && (p_ccb->fcrb.last_ack_sent != p_ccb->fcrb.next_seq_expected) ) + { +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.xmit_ack_touts++; +#endif + if (p_ccb->fcrb.local_busy) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + } +} + + +/******************************************************************************* +** +** Function process_reqseq +** +** Description Handle receive sequence number +** +** Returns - +** +*******************************************************************************/ +static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 req_seq, num_bufs_acked, xx; + UINT16 ls; + UINT16 full_sdus_xmitted; + + /* Receive sequence number does not ack anything for SREJ with P-bit set to zero */ + if ( (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + && ((ctrl_word & L2CAP_FCR_SUP_BITS) == (L2CAP_FCR_SUP_SREJ << L2CAP_FCR_SUP_SHIFT)) + && ((ctrl_word & L2CAP_FCR_P_BIT) == 0) ) + { + /* If anything still waiting for ack, restart the timer if it was stopped */ + if (p_fcrb->waiting_for_ack_q.count) + l2c_fcr_start_timer (p_ccb); + + return (TRUE); + } + + /* Extract the receive sequence number from the control word */ + req_seq = (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT; + + num_bufs_acked = (req_seq - p_fcrb->last_rx_ack) & L2CAP_FCR_SEQ_MODULO; + + /* Verify the request sequence is in range before proceeding */ + if (num_bufs_acked > p_fcrb->waiting_for_ack_q.count) + { + /* The channel is closed if ReqSeq is not in range */ + L2CAP_TRACE_WARNING4 ("L2CAP eRTM Frame BAD Req_Seq - ctrl_word: 0x%04x req_seq 0x%02x last_rx_ack: 0x%02x QCount: %u", + ctrl_word, req_seq, p_fcrb->last_rx_ack, p_fcrb->waiting_for_ack_q.count); + + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + +/* + L2CAP_TRACE_DEBUG3 ("L2CAP process_reqseq 0x%02x last_rx_ack: 0x%02x QCount: %u", + req_seq, p_fcrb->last_rx_ack, p_fcrb->waiting_for_ack_q.count); +*/ + p_fcrb->last_rx_ack = req_seq; + + /* Now we can release all acknowledged frames, and restart the retransmission timer if needed */ + if (num_bufs_acked != 0) + { + p_fcrb->num_tries = 0; + full_sdus_xmitted = 0; + +#if (L2CAP_ERTM_STATS == TRUE) + l2c_fcr_collect_ack_delay (p_ccb, num_bufs_acked); +#endif + + for (xx = 0; xx < num_bufs_acked; xx++) + { + ls = ((BT_HDR *)(p_fcrb->waiting_for_ack_q.p_first))->layer_specific & L2CAP_FCR_SAR_BITS; + + if ( (ls == L2CAP_FCR_UNSEG_SDU) || (ls == L2CAP_FCR_END_SDU) ) + full_sdus_xmitted++; + + GKI_freebuf (GKI_dequeue (&p_fcrb->waiting_for_ack_q)); + } + + /* If we are still in a wait_ack state, do not mess with the timer */ + if (!p_ccb->fcrb.wait_ack) + l2c_fcr_stop_timer (p_ccb); + + /* Check if we need to call the "packet_sent" callback */ + if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (full_sdus_xmitted) ) + { + /* Special case for eRTM, if all packets sent, send 0xFFFF */ + if ( (p_fcrb->waiting_for_ack_q.count == 0) && (p_ccb->xmit_hold_q.count == 0) ) + full_sdus_xmitted = 0xFFFF; + + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, full_sdus_xmitted); + } + } + + /* If anything still waiting for ack, restart the timer if it was stopped */ + if (p_fcrb->waiting_for_ack_q.count) + l2c_fcr_start_timer (p_ccb); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function process_s_frame +** +** Description Process an S frame +** +** Returns - +** +*******************************************************************************/ +static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT16 s_frame_type = (ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT; + BOOLEAN remote_was_busy; + BOOLEAN all_ok = TRUE; + + if (p_buf->len != 0) + { + L2CAP_TRACE_WARNING1 ("Incorrect S-frame Length (%d)", p_buf->len); + } + + L2CAP_TRACE_DEBUG2 ("process_s_frame ctrl_word 0x%04x fcrb_remote_busy:%d", ctrl_word, p_fcrb->remote_busy); + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.s_frames_rcvd[s_frame_type]++; +#endif + + if (ctrl_word & L2CAP_FCR_P_BIT) + { + p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send anoher REJ */ + p_fcrb->send_f_rsp = TRUE; /* Set a flag in case an I-frame is pending */ + } + + switch (s_frame_type) + { + case L2CAP_FCR_SUP_RR: + remote_was_busy = p_fcrb->remote_busy; + p_fcrb->remote_busy = FALSE; + + if ( (ctrl_word & L2CAP_FCR_F_BIT) || (remote_was_busy) ) + all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS); + break; + + case L2CAP_FCR_SUP_REJ: + p_fcrb->remote_busy = FALSE; + all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS); + break; + + case L2CAP_FCR_SUP_RNR: + p_fcrb->remote_busy = TRUE; + l2c_fcr_stop_timer (p_ccb); + break; + + case L2CAP_FCR_SUP_SREJ: + p_fcrb->remote_busy = FALSE; + all_ok = retransmit_i_frames (p_ccb, (UINT8)((ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT)); + break; + } + + if (all_ok) + { + /* If polled, we need to respond with F-bit. Note, we may have sent a I-frame with the F-bit */ + if (p_fcrb->send_f_rsp) + { + if (p_fcrb->srej_sent) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT); + else if (p_fcrb->local_busy) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT); + + p_fcrb->send_f_rsp = FALSE; + } + } + else + { + L2CAP_TRACE_DEBUG0 ("process_s_frame hit_max_retries"); + } + + GKI_freebuf (p_buf); +} + + +/******************************************************************************* +** +** Function process_i_frame +** +** Description Process an I frame +** +** Returns - +** +*******************************************************************************/ +static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT8 tx_seq, num_lost, num_to_ack, next_srej; + + /* If we were doing checkpoint recovery, first retransmit all unacked I-frames */ + if (ctrl_word & L2CAP_FCR_F_BIT) + { + if (!retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS)) + { + GKI_freebuf(p_buf); + return; + } + } + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.ertm_pkt_counts[1]++; + p_ccb->fcrb.ertm_byte_counts[1] += p_buf->len; +#endif + + /* Extract the sequence number */ + tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + /* If we have flow controlled the peer, ignore any bad I-frames from him */ + if ( (tx_seq != p_fcrb->next_seq_expected) && (p_fcrb->local_busy) ) + { + L2CAP_TRACE_WARNING1 ("Dropping bad I-Frame since we flowed off, tx_seq:%u", tx_seq); + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + GKI_freebuf(p_buf); + return; + } + + /* If there are no free buffers in the user Rx queue, drop the */ + /* received buffer now before we update any sequence numbers */ + if (GKI_poolfreecount (p_ccb->ertm_info.user_rx_pool_id) == 0) + { + L2CAP_TRACE_WARNING4 ("L2CAP CID: 0x%04x Dropping I-Frame seq: %u User RX Pool: %u (Size: %u) has no free buffers!!", + p_ccb->local_cid, tx_seq, p_ccb->ertm_info.user_rx_pool_id, + GKI_poolcount (p_ccb->ertm_info.user_rx_pool_id)); + GKI_freebuf(p_buf); + return; + } + + /* Check if tx-sequence is the expected one */ + if (tx_seq != p_fcrb->next_seq_expected) + { + num_lost = (tx_seq - p_fcrb->next_seq_expected) & L2CAP_FCR_SEQ_MODULO; + + /* Is the frame a duplicate ? If so, just drop it */ + if (num_lost >= p_ccb->our_cfg.fcr.tx_win_sz) + { + /* Duplicate - simply drop it */ + L2CAP_TRACE_WARNING2 ("process_i_frame() Dropping Duplicate Frame tx_seq:%u ExpectedTxSeq %u", tx_seq, p_fcrb->next_seq_expected); + GKI_freebuf(p_buf); + } + else + { + L2CAP_TRACE_WARNING6 ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej: %u", + p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent, p_fcrb->srej_sent); + + if (p_fcrb->srej_sent) + { + /* If SREJ sent, save the frame for later processing as long as it is in sequence */ + next_srej = (((BT_HDR *)p_fcrb->srej_rcv_hold_q.p_last)->layer_specific + 1) & L2CAP_FCR_SEQ_MODULO; + + if ( (tx_seq == next_srej) && (p_fcrb->srej_rcv_hold_q.count < p_ccb->our_cfg.fcr.tx_win_sz) ) + { + /* If user gave us a pool for held rx buffers, use that */ + if (p_ccb->ertm_info.fcr_rx_pool_id != HCI_ACL_POOL_ID) + { + BT_HDR *p_buf2; + + /* Adjust offset and len so that control word is copied */ + p_buf->offset -= L2CAP_FCR_OVERHEAD; + p_buf->len += L2CAP_FCR_OVERHEAD; + + p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len, p_ccb->ertm_info.fcr_rx_pool_id); + + if (p_buf2) + { + GKI_freebuf (p_buf); + p_buf = p_buf2; + } + p_buf->offset += L2CAP_FCR_OVERHEAD; + p_buf->len -= L2CAP_FCR_OVERHEAD; + } + L2CAP_TRACE_DEBUG4 ("process_i_frame() Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej1", + num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent); + + p_buf->layer_specific = tx_seq; + GKI_enqueue (&p_fcrb->srej_rcv_hold_q, p_buf); + } + else + { + L2CAP_TRACE_WARNING4 ("process_i_frame() CID: 0x%04x frame dropped in Srej Sent next_srej:%u hold_q.count:%u win_sz:%u", + p_ccb->local_cid, next_srej, p_fcrb->srej_rcv_hold_q.count, p_ccb->our_cfg.fcr.tx_win_sz); + + p_fcrb->rej_after_srej = TRUE; + GKI_freebuf (p_buf); + } + } + else if (p_fcrb->rej_sent) + { + L2CAP_TRACE_WARNING5 ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: 1 SRej: %u", + p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->srej_sent); + + /* If REJ sent, just drop the frame */ + GKI_freebuf (p_buf); + } + else + { + L2CAP_TRACE_DEBUG4 ("process_i_frame() CID: 0x%04x tx_seq:%u ExpTxSeq %u Rej: %u", + p_ccb->local_cid, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent); + + /* If only one lost, we will send SREJ, otherwise we will send REJ */ + if (num_lost > 1) + { + GKI_freebuf (p_buf); + p_fcrb->rej_sent = TRUE; + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0); + } + else + { + if (p_fcrb->srej_rcv_hold_q.count != 0) + { + L2CAP_TRACE_ERROR3 ("process_i_frame() CID: 0x%04x sending SREJ tx_seq:%d hold_q.count:%u", + p_ccb->local_cid, tx_seq, p_fcrb->srej_rcv_hold_q.count); + } + p_buf->layer_specific = tx_seq; + GKI_enqueue (&p_fcrb->srej_rcv_hold_q, p_buf); + p_fcrb->srej_sent = TRUE; + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, 0); + } + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + } + } + return; + } + + /* Seq number is the next expected. Clear possible reject exception in case it occured */ + p_fcrb->rej_sent = p_fcrb->srej_sent = FALSE; + + /* Adjust the next_seq, so that if the upper layer sends more data in the callback + context, the received frame is acked by an I-frame. */ + p_fcrb->next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + + /* If any SAR problem in eRTM mode, spec says disconnect. */ + if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) + { + L2CAP_TRACE_WARNING1 ("process_i_frame() CID: 0x%04x reassembly failed", p_ccb->local_cid); + l2cu_disconnect_chnl (p_ccb); + return; + } + + /* RR optimization - if peer can still send us more, then start an ACK timer */ + num_to_ack = (p_fcrb->next_seq_expected - p_fcrb->last_ack_sent) & L2CAP_FCR_SEQ_MODULO; + + if ( (num_to_ack < p_ccb->fcrb.max_held_acks) && (!p_fcrb->local_busy) ) + delay_ack = TRUE; + + /* We should neve never ack frame if we are not in OPEN state */ + if ((num_to_ack != 0) && p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) + { + /* If no frames are awaiting transmission or are held, send an RR or RNR S-frame for ack */ + if (delay_ack) + { + /* If it is the first I frame we did not ack, start ack timer */ + if (!p_ccb->fcrb.ack_timer.in_use) + { + btu_start_quick_timer (&p_ccb->fcrb.ack_timer, BTU_TTYPE_L2CAP_FCR_ACK, + (L2CAP_FCR_ACK_TOUT*QUICK_TIMER_TICKS_PER_SEC)/1000); + } + } + else if ( ((p_ccb->xmit_hold_q.count == 0) || (l2c_fcr_is_flow_controlled (p_ccb))) + && (p_ccb->fcrb.srej_rcv_hold_q.count == 0) ) + { + if (p_fcrb->local_busy) + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0); + else + l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0); + } + } +} + + +/******************************************************************************* +** +** Function process_stream_frame +** +** Description This function processes frames in streaming mode +** +** Returns - +** +*******************************************************************************/ +static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + UINT16 ctrl_word; + UINT16 fcs; + UINT8 *p; + UINT8 tx_seq; + + /* Verify FCS if using */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + { + p = ((UINT8 *)(p_buf+1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN; + + /* Extract and drop the FCS from the packet */ + STREAM_TO_UINT16 (fcs, p); + p_buf->len -= L2CAP_FCS_LEN; + + if (l2c_fcr_rx_get_fcs(p_buf) != fcs) + { + L2CAP_TRACE_WARNING1 ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid); + GKI_freebuf(p_buf); + return; + } + } + + /* Get the control word */ + p = ((UINT8 *)(p_buf+1)) + p_buf->offset; + + STREAM_TO_UINT16 (ctrl_word, p); + + p_buf->len -= L2CAP_FCR_OVERHEAD; + p_buf->offset += L2CAP_FCR_OVERHEAD; + + /* Make sure it is an I-frame */ + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + { + L2CAP_TRACE_WARNING2 ("Rx L2CAP PDU: CID: 0x%04x BAD S-frame in streaming mode ctrl_word: 0x%04x", p_ccb->local_cid, ctrl_word); + GKI_freebuf (p_buf); + return; + } + +#if BT_TRACE_VERBOSE == TRUE + L2CAP_TRACE_EVENT6 ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u", + p_ccb->local_cid, p_buf->len, + SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT], + (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT, + (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT); +#endif + + /* Extract the sequence number */ + tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + /* Check if tx-sequence is the expected one */ + if (tx_seq != p_ccb->fcrb.next_seq_expected) + { + L2CAP_TRACE_WARNING4 ("Rx L2CAP PDU: CID: 0x%04x Lost frames Exp: %u Got: %u p_rx_sdu: 0x%08x", + p_ccb->local_cid, p_ccb->fcrb.next_seq_expected, tx_seq, p_ccb->fcrb.p_rx_sdu); + + /* Lost one or more packets, so flush the SAR queue */ + if (p_ccb->fcrb.p_rx_sdu != NULL) + { + GKI_freebuf (p_ccb->fcrb.p_rx_sdu); + p_ccb->fcrb.p_rx_sdu = NULL; + } + } + + p_ccb->fcrb.next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO; + + if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) + { + /* Some sort of SAR error, so flush the SAR queue */ + if (p_ccb->fcrb.p_rx_sdu != NULL) + { + GKI_freebuf (p_ccb->fcrb.p_rx_sdu); + p_ccb->fcrb.p_rx_sdu = NULL; + } + } +} + + +/******************************************************************************* +** +** Function do_sar_reassembly +** +** Description Process SAR bits and re-assemble frame +** +** Returns TRUE if all OK, else FALSE +** +*******************************************************************************/ +static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word) +{ + tL2C_FCRB *p_fcrb = &p_ccb->fcrb; + UINT16 sar_type = ctrl_word & L2CAP_FCR_SEG_BITS; + BOOLEAN packet_ok = TRUE; + UINT8 *p; + + /* Check if the SAR state is correct */ + if ((sar_type == L2CAP_FCR_UNSEG_SDU) || (sar_type == L2CAP_FCR_START_SDU)) + { + if (p_fcrb->p_rx_sdu != NULL) + { + L2CAP_TRACE_WARNING2 ("SAR - got unexpected unsegmented or start SDU Expected len: %u Got so far: %u", + p_fcrb->rx_sdu_len, p_fcrb->p_rx_sdu->len); + + packet_ok = FALSE; + } + /* Check the length of the packet */ + if ( (sar_type == L2CAP_FCR_START_SDU) && (p_buf->len < L2CAP_SDU_LEN_OVERHEAD) ) + { + L2CAP_TRACE_WARNING1 ("SAR start packet too short: %u", p_buf->len); + packet_ok = FALSE; + } + } + else + { + if (p_fcrb->p_rx_sdu == NULL) + { + L2CAP_TRACE_WARNING0 ("SAR - got unexpected cont or end SDU"); + packet_ok = FALSE; + } + } + + if ( (packet_ok) && (sar_type != L2CAP_FCR_UNSEG_SDU) ) + { + p = ((UINT8 *)(p_buf + 1)) + p_buf->offset; + + /* For start SDU packet, extract the SDU length */ + if (sar_type == L2CAP_FCR_START_SDU) + { + /* Get the SDU length */ + STREAM_TO_UINT16 (p_fcrb->rx_sdu_len, p); + p_buf->offset += 2; + p_buf->len -= 2; + + if (p_fcrb->rx_sdu_len > p_ccb->max_rx_mtu) + { + L2CAP_TRACE_WARNING2 ("SAR - SDU len: %u larger than MTU: %u", p_fcrb->rx_sdu_len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } + else if ((p_fcrb->p_rx_sdu = (BT_HDR *)GKI_getpoolbuf (p_ccb->ertm_info.user_rx_pool_id)) == NULL) + { + L2CAP_TRACE_ERROR1 ("SAR - no buffer for SDU start user_rx_pool_id:%d", p_ccb->ertm_info.user_rx_pool_id); + packet_ok = FALSE; + } + else + { + p_fcrb->p_rx_sdu->offset = 4; /* this is the minimal offset required by OBX to process incoming packets */ + p_fcrb->p_rx_sdu->len = 0; + } + } + + if (packet_ok) + { + if ((p_fcrb->p_rx_sdu->len + p_buf->len) > p_fcrb->rx_sdu_len) + { + L2CAP_TRACE_ERROR4 ("SAR - SDU len exceeded Type: %u Lengths: %u %u %u", + sar_type, p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } + else if ( (sar_type == L2CAP_FCR_END_SDU) && ((p_fcrb->p_rx_sdu->len + p_buf->len) != p_fcrb->rx_sdu_len) ) + { + L2CAP_TRACE_WARNING3 ("SAR - SDU end rcvd but SDU incomplete: %u %u %u", + p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len); + packet_ok = FALSE; + } + else + { + memcpy (((UINT8 *) (p_fcrb->p_rx_sdu + 1)) + p_fcrb->p_rx_sdu->offset + p_fcrb->p_rx_sdu->len, p, p_buf->len); + + p_fcrb->p_rx_sdu->len += p_buf->len; + + GKI_freebuf (p_buf); + p_buf = NULL; + + if (sar_type == L2CAP_FCR_END_SDU) + { + p_buf = p_fcrb->p_rx_sdu; + p_fcrb->p_rx_sdu = NULL; + } + } + } + } + + if (packet_ok == FALSE) + { + GKI_freebuf (p_buf); + } + else if (p_buf != NULL) + { +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (p_ccb->local_cid < L2CAP_BASE_APPL_CID) + (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(p_ccb->p_lcb->remote_bd_addr, p_buf); + else +#endif + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_buf); + } + + return (packet_ok); +} + + +/******************************************************************************* +** +** Function retransmit_i_frames +** +** Description This function retransmits i-frames awaiting acks. +** +** Returns BOOLEAN - TRUE if retransmitted +** +*******************************************************************************/ +static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq) +{ + BT_HDR *p_buf, *p_buf2; + UINT8 *p; + UINT8 buf_seq; + UINT16 ctrl_word; + + if ( (p_ccb->fcrb.waiting_for_ack_q.p_first) + && (p_ccb->peer_cfg.fcr.max_transmit != 0) + && (p_ccb->fcrb.num_tries >= p_ccb->peer_cfg.fcr.max_transmit) ) + { + L2CAP_TRACE_EVENT5 ("Max Tries Exceeded: (last_acq: %d CID: 0x%04x num_tries: %u (max: %u) ack_q_count: %u", + p_ccb->fcrb.last_rx_ack, p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit, + p_ccb->fcrb.waiting_for_ack_q.count); + + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + + /* tx_seq indicates whether to retransmit a specific sequence or all (if == L2C_FCR_RETX_ALL_PKTS) */ + if (tx_seq != L2C_FCR_RETX_ALL_PKTS) + { + /* If sending only one, the sequence number tells us which one. Look for it. + */ + for (p_buf = (BT_HDR *)p_ccb->fcrb.waiting_for_ack_q.p_first; p_buf; p_buf = (BT_HDR *)GKI_getnext (p_buf)) + { + /* Get the old control word */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + + STREAM_TO_UINT16 (ctrl_word, p); + + buf_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT; + + L2CAP_TRACE_DEBUG2 ("retransmit_i_frames() cur seq: %u looking for: %u", buf_seq, tx_seq); + + if (tx_seq == buf_seq) + break; + } + + if (!p_buf) + { + L2CAP_TRACE_ERROR2 ("retransmit_i_frames() UNKNOWN seq: %u q_count: %u", tx_seq, p_ccb->fcrb.waiting_for_ack_q.count); + return (TRUE); + } + } + else + { + /* Retransmitting everything. Flush buffers we already put in the link xmit queue. + */ + p_buf = (BT_HDR *)p_ccb->p_lcb->link_xmit_data_q.p_first; + + while (p_buf != NULL) + { + /* Do not flush other CIDs or partial segments */ + if ( (p_buf->layer_specific == 0) && (p_buf->event == p_ccb->local_cid) ) + { + p_buf2 = p_buf; + p_buf = (BT_HDR *)GKI_getnext (p_buf); + + GKI_remove_from_queue (&p_ccb->p_lcb->link_xmit_data_q, p_buf2); + GKI_freebuf (p_buf2); + } + else + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + + /* Also flush our retransmission queue */ + while (p_ccb->fcrb.retrans_q.p_first) + GKI_freebuf (GKI_dequeue (&p_ccb->fcrb.retrans_q)); + + p_buf = (BT_HDR *)p_ccb->fcrb.waiting_for_ack_q.p_first; + } + + while (p_buf != NULL) + { + p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len, p_ccb->ertm_info.fcr_tx_pool_id); + + if (p_buf2) + { + p_buf2->layer_specific = p_buf->layer_specific; + + GKI_enqueue (&p_ccb->fcrb.retrans_q, p_buf2); + } + + if ( (tx_seq != L2C_FCR_RETX_ALL_PKTS) || (p_buf2 == NULL) ) + break; + else + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + + if (p_ccb->fcrb.waiting_for_ack_q.count) + { + p_ccb->fcrb.num_tries++; + l2c_fcr_start_timer (p_ccb); + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_get_next_xmit_sdu_seg +** +** Description Get the next SDU segment to transmit. +** +** Returns pointer to buffer with segment or NULL +** +*******************************************************************************/ +BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length) +{ + BOOLEAN first_seg = FALSE, /* The segment is the first part of data */ + mid_seg = FALSE, /* The segment is the middle part of data */ + last_seg = FALSE; /* The segment is the last part of data */ + UINT16 sdu_len; + BT_HDR *p_buf, *p_xmit; + UINT8 *p; + UINT16 max_pdu = p_ccb->tx_mps /* Needed? - L2CAP_MAX_HEADER_FCS*/; + + /* If there is anything in the retransmit queue, that goes first + */ + if (p_ccb->fcrb.retrans_q.p_first) + { + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->fcrb.retrans_q); + + /* Update Rx Seq and FCS if we acked some packets while this one was queued */ + prepare_I_frame (p_ccb, p_buf, TRUE); + + p_buf->event = p_ccb->local_cid; + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + /* Use sdu_len to hold the control word */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD; + STREAM_TO_UINT16 (sdu_len, p); + + /* Get out if packet was dropped; just pretend it went out */ + if (l2c_corrupt_the_fcr_packet (p_ccb, p_buf, FALSE, sdu_len)) + return (NULL); +#endif /* L2CAP_CORRUPT_ERTM_PKTS */ + +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.pkts_retransmitted++; + p_ccb->fcrb.ertm_pkt_counts[0]++; + p_ccb->fcrb.ertm_byte_counts[0] += (p_buf->len - 8); +#endif + return (p_buf); + } + + /* For BD/EDR controller, max_packet_length is set to 0 */ + /* For AMP controller, max_packet_length is set by available blocks */ + if ( (max_packet_length > L2CAP_MAX_HEADER_FCS) + && (max_pdu + L2CAP_MAX_HEADER_FCS > max_packet_length) ) + { + max_pdu = max_packet_length - L2CAP_MAX_HEADER_FCS; + } + + p_buf = (BT_HDR *)p_ccb->xmit_hold_q.p_first; + + /* If there is more data than the MPS, it requires segmentation */ + if (p_buf->len > max_pdu) + { + /* We are using the "event" field to tell is if we already started segmentation */ + if (p_buf->event == 0) + { + first_seg = TRUE; + sdu_len = p_buf->len; + } + else + mid_seg = TRUE; + + /* Get a new buffer and copy the data that can be sent in a PDU */ + p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET, + max_pdu, p_ccb->ertm_info.fcr_tx_pool_id); + + if (p_xmit != NULL) + { + p_buf->event = p_ccb->local_cid; + p_xmit->event = p_ccb->local_cid; + + p_buf->len -= max_pdu; + p_buf->offset += max_pdu; + + /* copy PBF setting */ + p_xmit->layer_specific = p_buf->layer_specific; + } + else /* Should never happen if the application has configured buffers correctly */ + { + L2CAP_TRACE_ERROR1 ("L2CAP - cannot get buffer, for segmentation, pool: %u", p_ccb->ertm_info.fcr_tx_pool_id); + return (NULL); + } + } + else /* Use the original buffer if no segmentation, or the last segment */ + { + p_xmit = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q); + + if (p_xmit->event != 0) + last_seg = TRUE; + + p_xmit->event = p_ccb->local_cid; + } + + /* Step back to add the L2CAP headers */ + p_xmit->offset -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD); + p_xmit->len += L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD; + + if (first_seg) + { + p_xmit->offset -= L2CAP_SDU_LEN_OVERHEAD; + p_xmit->len += L2CAP_SDU_LEN_OVERHEAD; + } + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_xmit + 1) + p_xmit->offset; + + /* Now the L2CAP header */ + + /* Note: if FCS has to be included then the length is recalculated later */ + UINT16_TO_STREAM (p, p_xmit->len - L2CAP_PKT_OVERHEAD); + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + + if (first_seg) + { + /* Skip control word and add SDU length */ + p += 2; + UINT16_TO_STREAM (p, sdu_len); + + /* We will store the SAR type in layer-specific */ + /* layer_specific is shared with flushable flag(bits 0-1), don't clear it */ + p_xmit->layer_specific |= L2CAP_FCR_START_SDU; + + first_seg = FALSE; + } + else if (mid_seg) + p_xmit->layer_specific |= L2CAP_FCR_CONT_SDU; + else if (last_seg) + p_xmit->layer_specific |= L2CAP_FCR_END_SDU; + else + p_xmit->layer_specific |= L2CAP_FCR_UNSEG_SDU; + + prepare_I_frame (p_ccb, p_xmit, FALSE); + + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + { + BT_HDR *p_wack = l2c_fcr_clone_buf (p_xmit, HCI_DATA_PREAMBLE_SIZE, p_xmit->len, p_ccb->ertm_info.fcr_tx_pool_id); + + if (!p_wack) + { + L2CAP_TRACE_ERROR3 ("L2CAP - no buffer for xmit cloning, CID: 0x%04x Pool: %u Count: %u", + p_ccb->local_cid, p_ccb->ertm_info.fcr_tx_pool_id, GKI_poolfreecount(p_ccb->ertm_info.fcr_tx_pool_id)); + + /* We will not save the FCS in case we reconfigure and change options */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + p_xmit->len -= L2CAP_FCS_LEN; + + /* Pretend we sent it and it got lost */ + GKI_enqueue (&p_ccb->fcrb.waiting_for_ack_q, p_xmit); + return (NULL); + } + else + { +#if (L2CAP_ERTM_STATS == TRUE) + /* set timestamp at the end of tx I-frame to get acking delay */ + p = ((UINT8 *) (p_wack+1)) + p_wack->offset + p_wack->len; + UINT32_TO_STREAM (p, GKI_get_os_tick_count()); +#endif + /* We will not save the FCS in case we reconfigure and change options */ + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + p_wack->len -= L2CAP_FCS_LEN; + + p_wack->layer_specific = p_xmit->layer_specific; + GKI_enqueue (&p_ccb->fcrb.waiting_for_ack_q, p_wack); + } + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + { + UINT16 ctrl_word; + p = ((UINT8 *) (p_xmit+1)) + p_xmit->offset + L2CAP_PKT_OVERHEAD; + STREAM_TO_UINT16 (ctrl_word, p); + + /* Get out if packet was dropped; pretend it was sent */ + if (l2c_corrupt_the_fcr_packet (p_ccb, p_xmit, FALSE, ctrl_word)) + return (NULL); + } +#endif +#if (L2CAP_ERTM_STATS == TRUE) + p_ccb->fcrb.ertm_pkt_counts[0]++; + p_ccb->fcrb.ertm_byte_counts[0] += (p_xmit->len - 8); +#endif + + } + + return (p_xmit); +} + + +/******************************************************************************* +** Configuration negotiation functions +** +** The following functions are used in negotiating channel modes during +** configuration +********************************************************************************/ + +/******************************************************************************* +** +** Function l2c_fcr_chk_chan_modes +** +** Description Validates and adjusts if necessary, the FCR options +** based on remote EXT features. +** +** Note: This assumes peer EXT Features have been received. +** Basic mode is used if FCR Options have not been received +** +** Returns UINT8 - nonzero if can continue, '0' if no compatible channels +** +*******************************************************************************/ +UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb) +{ + /* Remove nonbasic options that the peer does not support */ + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_ENH_RETRANS)) + p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_ERTM; + + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_STREAM_MODE)) + p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_STREAM; + + /* At least one type needs to be set (Basic, ERTM, STM) to continue */ + if (!p_ccb->ertm_info.allowed_modes) + { + L2CAP_TRACE_WARNING0 ("L2CAP - Peer does not support our desired channel types"); + } + + return (p_ccb->ertm_info.allowed_modes); +} + +/******************************************************************************* +** +** Function l2c_fcr_adj_our_req_options +** +** Description Validates and sets up the FCR options passed in from +** L2CA_ConfigReq based on remote device's features. +** +** Returns TRUE if no errors, Otherwise FALSE +** +*******************************************************************************/ +BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + tL2CAP_FCR_OPTS *p_fcr = &p_cfg->fcr; + + if (p_fcr->mode != p_ccb->ertm_info.preferred_mode) + { + L2CAP_TRACE_WARNING2 ("l2c_fcr_adj_our_req_options - preferred_mode (%d), does not match mode (%d)", + p_ccb->ertm_info.preferred_mode, p_fcr->mode); + + /* The preferred mode is passed in through tL2CAP_ERTM_INFO, so override this one */ + p_fcr->mode = p_ccb->ertm_info.preferred_mode; + } + + /* If upper layer did not request eRTM mode, BASIC must be used */ + if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) + { + if (p_cfg->fcr_present && p_fcr->mode != L2CAP_FCR_BASIC_MODE) + { + L2CAP_TRACE_WARNING1 ("l2c_fcr_adj_our_req_options (mode %d): ERROR: No FCR options set using BASIC mode", p_fcr->mode); + } + p_fcr->mode = L2CAP_FCR_BASIC_MODE; + } + + /* Process the FCR options if initial channel bring-up (not a reconfig request) + ** Determine initial channel mode to try based on our options and remote's features + */ + if (p_cfg->fcr_present && !(p_ccb->config_done & RECONFIG_FLAG)) + { + /* We need to have at least one mode type common with the peer */ + if (!l2c_fcr_chk_chan_modes(p_ccb)) + { + /* Two channels have incompatible supported types */ + l2cu_disconnect_chnl (p_ccb); + return (FALSE); + } + + /* Basic is the only common channel mode between the two devices */ + else if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) + { + /* We only want to try Basic, so bypass sending the FCR options entirely */ + p_cfg->fcr_present = FALSE; + p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */ + p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */ + } + + /* We have at least one non-basic mode available + * Override mode from available mode options based on preference, if needed + */ + else + { + /* If peer does not support STREAMING, try ERTM */ + if (p_fcr->mode == L2CAP_FCR_STREAM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_STREAM)) + { + L2CAP_TRACE_DEBUG0 ("L2C CFG: mode is STREAM, but peer does not support; Try ERTM"); + p_fcr->mode = L2CAP_FCR_ERTM_MODE; + } + + /* If peer does not support ERTM, try BASIC (will support this if made it here in the code) */ + if (p_fcr->mode == L2CAP_FCR_ERTM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM)) + { + L2CAP_TRACE_DEBUG0 ("L2C CFG: mode is ERTM, but peer does not support; Try BASIC"); + p_fcr->mode = L2CAP_FCR_BASIC_MODE; + } + } + + if (p_fcr->mode != L2CAP_FCR_BASIC_MODE) + { + /* MTU must be smaller than buffer size */ + if ( (p_cfg->mtu_present) && (p_cfg->mtu > p_ccb->max_rx_mtu) ) + { + L2CAP_TRACE_WARNING2 ("L2CAP - MTU: %u larger than buf size: %u", p_cfg->mtu, p_ccb->max_rx_mtu); + return (FALSE); + } + + /* application want to use the default MPS */ + if (p_fcr->mps == L2CAP_DEFAULT_ERM_MPS) + { + p_fcr->mps = L2CAP_MPS_OVER_BR_EDR; + } + /* MPS must be less than MTU */ + else if (p_fcr->mps > p_ccb->max_rx_mtu) + { + L2CAP_TRACE_WARNING2 ("L2CAP - MPS %u invalid MTU: %u", p_fcr->mps, p_ccb->max_rx_mtu); + return (FALSE); + } + + /* We always initially read into the HCI buffer pool, so make sure it fits */ + if (p_fcr->mps > (L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS)) + p_fcr->mps = L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS; + } + else + { + p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */ + p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */ + } + + p_ccb->our_cfg.fcr = *p_fcr; + } + else /* Not sure how to send a reconfiguration(??) should fcr be included? */ + { + p_ccb->our_cfg.fcr_present = FALSE; + } + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_adj_monitor_retran_timeout +** +** Description Overrides monitor/retrans timer value based on controller +** +** Returns None +** +*******************************************************************************/ +void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb) +{ + /* adjust our monitor/retran timeout */ + if (p_ccb->out_cfg_fcr_present) + { + /* + ** if we requestd ERTM or accepted ERTM + ** We may accept ERTM even if we didn't request ERTM, in case of requesting STREAM + */ + if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) + ||(p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)) + { + /* upper layer setting is ignored */ + p_ccb->our_cfg.fcr.mon_tout = L2CAP_MIN_MONITOR_TOUT; + p_ccb->our_cfg.fcr.rtrans_tout = L2CAP_MIN_RETRANS_TOUT; + } + else + { + p_ccb->our_cfg.fcr.mon_tout = 0; + p_ccb->our_cfg.fcr.rtrans_tout = 0; + } + + L2CAP_TRACE_DEBUG2 ("l2c_fcr_adj_monitor_retran_timeout: mon_tout:%d, rtrans_tout:%d", + p_ccb->our_cfg.fcr.mon_tout, p_ccb->our_cfg.fcr.rtrans_tout); + } +} +/******************************************************************************* +** +** Function l2c_fcr_adj_our_rsp_options +** +** Description Overrides any neccesary FCR options passed in from +** L2CA_ConfigRsp based on our FCR options. +** Only makes adjustments if channel is in ERTM mode. +** +** Returns None +** +*******************************************************************************/ +void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + /* adjust our monitor/retran timeout */ + l2c_fcr_adj_monitor_retran_timeout(p_ccb); + + p_cfg->fcr_present = p_ccb->out_cfg_fcr_present; + + if (p_cfg->fcr_present) + { +// btla-specific ++ + /* Temporary - until a better algorithm is implemented */ + /* If peer's tx_wnd_sz requires too many buffers for us to support, then adjust it. For now, respond with our own tx_wnd_sz. */ + /* Note: peer is not guaranteed to obey our adjustment */ + if (p_ccb->peer_cfg.fcr.tx_win_sz > p_ccb->our_cfg.fcr.tx_win_sz) + { + L2CAP_TRACE_DEBUG3 ("%s: adjusting requested tx_win_sz from %i to %i", __FUNCTION__, p_ccb->peer_cfg.fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz); + p_ccb->peer_cfg.fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz; + } +// btla-specific -- + + p_cfg->fcr.mode = p_ccb->peer_cfg.fcr.mode; + p_cfg->fcr.tx_win_sz = p_ccb->peer_cfg.fcr.tx_win_sz; + p_cfg->fcr.max_transmit = p_ccb->peer_cfg.fcr.max_transmit; + p_cfg->fcr.mps = p_ccb->peer_cfg.fcr.mps; + p_cfg->fcr.rtrans_tout = p_ccb->our_cfg.fcr.rtrans_tout; + p_cfg->fcr.mon_tout = p_ccb->our_cfg.fcr.mon_tout; + } +} + +/******************************************************************************* +** +** Function l2c_fcr_renegotiate_chan +** +** Description Called upon unsuccessful peer response to config request. +** If the error is because of the channel mode, it will try +** to resend using another supported optional channel. +** +** Returns TRUE if resent configuration, False if channel matches or +** cannot match. +** +*******************************************************************************/ +BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + UINT8 peer_mode = p_ccb->our_cfg.fcr.mode; + BOOLEAN can_renegotiate; + + /* Skip if this is a reconfiguration from OPEN STATE or if FCR is not returned */ + if (!p_cfg->fcr_present || (p_ccb->config_done & RECONFIG_FLAG)) + return (FALSE); + + /* Only retry if there are more channel options to try */ + if (p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS) + { + peer_mode = (p_cfg->fcr_present) ? p_cfg->fcr.mode : L2CAP_FCR_BASIC_MODE; + + if (p_ccb->our_cfg.fcr.mode != peer_mode) + { + + if ((--p_ccb->fcr_cfg_tries) == 0) + { + p_cfg->result = L2CAP_CFG_FAILED_NO_REASON; + L2CAP_TRACE_WARNING0 ("l2c_fcr_renegotiate_chan (Max retries exceeded)"); + } + + can_renegotiate = FALSE; + + /* Try another supported mode if available based on our last attempted channel */ + switch (p_ccb->our_cfg.fcr.mode) + { + /* Our Streaming mode request was unnacceptable; try ERTM or Basic */ + case L2CAP_FCR_STREAM_MODE: + /* Peer wants ERTM and we support it */ + if ( (peer_mode == L2CAP_FCR_ERTM_MODE) && (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) + { + L2CAP_TRACE_DEBUG0 ("l2c_fcr_renegotiate_chan(Trying ERTM)"); + p_ccb->our_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE; + can_renegotiate = TRUE; + } + else /* Falls through */ + + case L2CAP_FCR_ERTM_MODE: + { + /* We can try basic for any other peer mode if we support it */ + if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) + { + L2CAP_TRACE_DEBUG0 ("l2c_fcr_renegotiate_chan(Trying Basic)"); + can_renegotiate = TRUE; + p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + } + } + break; + + default: + /* All other scenarios cannot be renegotiated */ + break; + } + + if (can_renegotiate) + { + p_ccb->our_cfg.fcr_present = TRUE; + + if (p_ccb->our_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) + { + p_ccb->our_cfg.fcs_present = FALSE; + p_ccb->our_cfg.ext_flow_spec_present = FALSE; + + /* Basic Mode uses ACL Data Pool, make sure the MTU fits */ + if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) + { + L2CAP_TRACE_WARNING1 ("L2CAP - adjust MTU: %u too large", p_cfg->mtu); + p_cfg->mtu = L2CAP_MTU_SIZE; + } + } + + l2cu_process_our_cfg_req (p_ccb, &p_ccb->our_cfg); + l2cu_send_peer_config_req (p_ccb, &p_ccb->our_cfg); + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT); + return (TRUE); + } + } + } + + /* Disconnect if the channels do not match */ + if (p_ccb->our_cfg.fcr.mode != peer_mode) + { + L2CAP_TRACE_WARNING2 ("L2C CFG: Channels incompatible (local %d, peer %d)", + p_ccb->our_cfg.fcr.mode, peer_mode); + l2cu_disconnect_chnl (p_ccb); + } + + return (FALSE); +} + + +/******************************************************************************* +** +** Function l2c_fcr_process_peer_cfg_req +** +** Description This function is called to process the FCR options passed +** in the peer's configuration request. +** +** Returns UINT8 - L2CAP_PEER_CFG_OK, L2CAP_PEER_CFG_UNACCEPTABLE, +** or L2CAP_PEER_CFG_DISCONNECT. +** +*******************************************************************************/ +UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + UINT16 max_retrans_size; + UINT8 fcr_ok = L2CAP_PEER_CFG_OK; + + p_ccb->p_lcb->w4_info_rsp = FALSE; /* Handles T61x SonyEricsson Bug in Info Request */ + + L2CAP_TRACE_EVENT5 ("l2c_fcr_process_peer_cfg_req() CFG fcr_present:%d fcr.mode:%d CCB FCR mode:%d preferred: %u allowed:%u", + p_cfg->fcr_present, p_cfg->fcr.mode, p_ccb->our_cfg.fcr.mode, p_ccb->ertm_info.preferred_mode, + p_ccb->ertm_info.allowed_modes); + + /* If Peer wants basic, we are done (accept it or disconnect) */ + if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) + { + /* If we do not allow basic, disconnect */ + if ( !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) ) + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + } + + /* Need to negotiate if our modes are not the same */ + else if (p_cfg->fcr.mode != p_ccb->ertm_info.preferred_mode) + { + /* If peer wants a mode that we don't support then retry our mode (ex. rtx/flc), OR + ** If we want ERTM and they wanted streaming retry our mode. + ** Note: If we have already determined they support our mode previously + ** from their EXF mask. + */ + if ( (((1 << p_cfg->fcr.mode) & L2CAP_FCR_CHAN_OPT_ALL_MASK) == 0) + || (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_ERTM_MODE) ) + { + p_cfg->fcr.mode = p_ccb->our_cfg.fcr.mode; + p_cfg->fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz; + p_cfg->fcr.max_transmit = p_ccb->our_cfg.fcr.max_transmit; + fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE; + } + + /* If we wanted basic, then try to renegotiate it */ + else if (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_BASIC_MODE) + { + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0; + p_cfg->fcr.rtrans_tout = p_cfg->fcr.mon_tout = p_cfg->fcr.mps = 0; + p_ccb->our_cfg.fcr.rtrans_tout = p_ccb->our_cfg.fcr.mon_tout = p_ccb->our_cfg.fcr.mps = 0; + fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE; + } + + /* Only other valid case is if they want ERTM and we wanted STM which should be + accepted if we support it; otherwise the channel should be disconnected */ + else if ( (p_cfg->fcr.mode != L2CAP_FCR_ERTM_MODE) + || !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) + { + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + } + } + + /* Configuration for FCR channels so make any adjustments and fwd to upper layer */ + if (fcr_ok == L2CAP_PEER_CFG_OK) + { + /* by default don't need to send params in the response */ + p_ccb->out_cfg_fcr_present = FALSE; + + /* Make any needed adjustments for the response to the peer */ + if (p_cfg->fcr_present && p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) + { + /* Peer desires to bypass FCS check, and streaming or ERTM mode */ + if (p_cfg->fcs_present) + { + p_ccb->peer_cfg.fcs = p_cfg->fcs; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCS; + if( p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) + p_ccb->bypass_fcs |= L2CAP_CFG_FCS_PEER; + } + + max_retrans_size = GKI_get_pool_bufsize (p_ccb->ertm_info.fcr_tx_pool_id) - sizeof(BT_HDR) + - L2CAP_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET - L2CAP_FCS_LEN; + + /* Ensure the MPS is not bigger than the MTU */ + if ( (p_cfg->fcr.mps == 0) || (p_cfg->fcr.mps > p_ccb->peer_cfg.mtu) ) + { + p_cfg->fcr.mps = p_ccb->peer_cfg.mtu; + p_ccb->out_cfg_fcr_present = TRUE; + } + + /* Ensure the MPS is not bigger than our retransmission buffer */ + if (p_cfg->fcr.mps > max_retrans_size) + { + L2CAP_TRACE_DEBUG2("CFG: Overriding MPS to %d (orig %d)", max_retrans_size, p_cfg->fcr.mps); + + p_cfg->fcr.mps = max_retrans_size; + p_ccb->out_cfg_fcr_present = TRUE; + } + + if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE) + { + /* Always respond with FCR ERTM parameters */ + p_ccb->out_cfg_fcr_present = TRUE; + } + } + + /* Everything ok, so save the peer's adjusted fcr options */ + p_ccb->peer_cfg.fcr = p_cfg->fcr; + + if (p_cfg->fcr_present) + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCR; + } + else if (fcr_ok == L2CAP_PEER_CFG_UNACCEPTABLE) + { + /* Allow peer only one retry for mode */ + if (p_ccb->peer_cfg_already_rejected) + fcr_ok = L2CAP_PEER_CFG_DISCONNECT; + else + p_ccb->peer_cfg_already_rejected = TRUE; + } + + return (fcr_ok); +} + + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE +/******************************************************************************* +** Functions used for testing ERTM mode +*/ +/* If FALSE, will also corrupt cid, length, control word, etc. */ +#ifndef L2CAP_CORR_FCS_ONLY +#define L2CAP_CORR_FCS_ONLY TRUE +#endif + +#define L2C_DISP_FRAME_SIZE 16 +/******************************************************************************* +** +** Function l2c_corrupt_the_fcr_packet +** +** Description This function is used for testing purposes only. +** It systematically or randomly corrupts packets used with +** ERTM channels. +** +** Returns BOOLEAN TRUE if packet was dropped, +** FALSE if fcs corrupted or no corruption +** +*******************************************************************************/ +static BOOLEAN l2c_corrupt_the_fcr_packet (tL2C_CCB *p_ccb, BT_HDR *p_buf, + BOOLEAN is_rx, UINT16 ctrl_word) +{ + tL2C_FCR_TEST_CFG *p_cfg; + UINT32 tc; + UINT8 *p; + UINT32 xx; + char buf[L2C_DISP_FRAME_SIZE]; + + if (!p_ccb || !p_ccb->fcrb.test_cb.cfg.in_use) + return FALSE; + + /* Prepare bad FCS */ + p = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + tc = GKI_get_os_tick_count(); + tc ^= p[p_buf->len - 1]; + xx = tc % 47; + + p_cfg = &p_ccb->fcrb.test_cb.cfg; +#if 0 + L2CAP_TRACE_DEBUG4 ("testcfg: type: %d, freq: %d (NRM-0, RDM-1), is_rx: %d, count: %d", + p_cfg->type, p_cfg->freq, p_cfg->is_rx, p_cfg->count); +#endif + /* If not time to corrupt get out */ + if (p_cfg->freq == L2CAP_FCR_FREQ_RANDOM) + { + if (xx != 0) + return FALSE; + } + else /* check test criteria before corrupting */ + { + if ( (p_cfg->count == 0) + || (p_cfg->is_rx != is_rx) + || ((UINT8)(ctrl_word & L2CAP_FCR_S_FRAME_BIT) != p_cfg->type) ) + { + return FALSE; + } + + /* Turn off if done */ + if (--(p_cfg->count) == 0) + { + p_ccb->fcrb.test_cb.cfg.in_use = FALSE; + } + } + +#if L2CAP_CORR_FCS_ONLY == TRUE + /* Corrupt the FCS check */ + p[p_buf->len - 1] = p[p_buf->len - 2] = 0; +#else + /* If made it this far packet needs to be corrupted */ + xx = tc % p_buf->len; + + /* Make sure the value was changed */ + if (p[xx + 1] == 0) + p[xx + 1] = 0x5A; + + p[xx] = p[xx] ^ p[xx + 1]; + +#if 1 /* Enable if not wishing to corrupting frame type */ + { + UINT8 *p_temp = ((UINT8 *) (p_buf + 1)) + p_buf->offset; + p_temp += L2CAP_PKT_OVERHEAD; + if ((UINT16)((*p_temp) & 0x01) != (ctrl_word & 0x0001)) + { + (*p_temp) |= (UINT8)(ctrl_word & 0x0001); + } + } +#endif +#endif + + if (is_rx) + { + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Rx S-Frame"); + else + BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Rx I-Frame"); + } + else + { + if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) + BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Tx S-Frame"); + else + BCM_STRCPY_S(buf, L2C_DISP_FRAME_SIZE, "Tx I-Frame"); + } + + /* Lastly, just drop packet if FCS is not being used or if Tx */ + if (!is_rx || p_ccb->bypass_fcs == L2CAP_BYPASS_FCS) + { + L2CAP_TRACE_ERROR6 ("-=-=-=-=-=-=-=- Dropping %s packet (0x%04x) tc: %u Buf Len: %u xx: %u count: %d", + buf, (UINT32)p_buf, tc, p_buf->len, xx, p_cfg->count); + GKI_freebuf(p_buf); + return TRUE; + } + else + { + L2CAP_TRACE_ERROR6 ("-=-=-=-=-=-=-=- Corrupting %s packet (0x%04x) tc: %u Buf Len: %u xx: %u count: %d", + buf, (UINT32)p_buf, tc, p_buf->len, xx, p_cfg->count); + } + + return FALSE; +} + + +/******************************************************************************* +** +** Function L2CA_SetupErtmTest +** +** Description This function is used for testing purposes only. +** It corrupts or drops one or more packets used with ERTM channels. +** +** Parameters +** cid - channel ID (0 uses RFCOMM PSM's CID) +** +** type - type of test to run (L2CAP_FCR_TTYPE_CORR_IFRAMES +** L2CAP_FCR_TTYPE_CORR_SFRAME +** L2CAP_FCR_TTYPE_STOP_TEST +** L2CAP_FCR_TTYPE_GET_CID - returns rfcomm cid only) +** +** is_rx - TRUE to corrupt Rx packet, FALSE for Tx packet) +** +** freq - L2CAP_FCR_FREQ_RANDOM (turns on random corruptions/drops) +** L2CAP_FCR_FREQ_NORMAL (turns on test with "count" corruptions/drops) +** +** count - number of packets in a row to drop or corrupt +** +** Returns CID of channel running test +** +*******************************************************************************/ +UINT16 L2CA_SetupErtmTest (UINT16 cid, UINT8 type, BOOLEAN is_rx, UINT8 freq, UINT16 count) +{ + tL2C_CCB *p_ccb = NULL; + tL2C_CCB *p_temp_ccb; + tL2C_FCR_TEST_CB *p_test_cb; + UINT16 xx; + + /* If '0' tests run on top of RFCOMM CID if active */ + if (cid == 0) + { + p_temp_ccb = l2cb.ccb_pool; + for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++, p_temp_ccb++) + { + /* Fixed channels don't have p_rcb's */ + if (p_temp_ccb->in_use && p_temp_ccb->p_rcb && p_temp_ccb->p_rcb->psm == BT_PSM_RFCOMM) + { + p_ccb = p_temp_ccb; + cid = L2CAP_BASE_APPL_CID + xx; + break; + } + } + } + else + p_ccb = l2cu_find_ccb_by_cid (NULL, cid); + + if (!p_ccb || type == L2CAP_FCR_TTYPE_GET_CID) + { + if (type == L2CAP_FCR_TTYPE_GET_CID) + { + L2CAP_TRACE_API1 ("L2CA_SetupErtmTest (GET_CID): cid = 0x%04x", cid); + } + return (cid); + } + + p_test_cb = &p_ccb->fcrb.test_cb; + + /* Turn off the current test */ + if (type == L2CAP_FCR_TTYPE_STOP_TEST) + { + if (p_test_cb->cfg.in_use) + { + L2CAP_TRACE_ERROR1 ("L2CA_SetupErtmTest (OFF): cid 0x%04x", cid); + } + p_test_cb->cfg.in_use = FALSE; + p_test_cb->cfg.count = 0; + } + + /* Process the new request */ + else if (!p_test_cb->cfg.in_use) + { + /* count must be positive unless random is used */ + if (!count && freq != L2CAP_FCR_FREQ_RANDOM) + { + L2CAP_TRACE_ERROR1 ("L2CA_SetupErtmTest (FAIL): Count = 0, freq = %d", freq); + return (cid); + } + + L2CAP_TRACE_ERROR5 ("L2CA_SetupErtmTest (START): cid 0x%04x, type %d, is_rx %d, freq %d, count %d", + cid, type, is_rx, freq, count); + + p_test_cb->cfg.in_use = TRUE; + p_test_cb->cfg.freq = freq; + p_test_cb->cfg.type = type; + p_test_cb->cfg.is_rx = is_rx; + p_test_cb->cfg.count = count; + } + else /* Test already in progress so ignore */ + { + L2CAP_TRACE_ERROR5 ("L2CA_SetupErtmTest (ignoring): cid 0x%04x, type %d, is_rx %d, freq %d, count %d", + cid, type, is_rx, freq, count); + } + + return (cid); +} + + +/******************************************************************************* +** The following routines are only used in conjunction with Conformance testing +********************************************************************************/ + +/******************************************************************************* +** +** Function L2CA_SendPolledSFrame +** +** Description This function is used for testing purposes only. +** It Sends a Polled RR or RNR to the peer +** +** Parameters +** cid - channel ID +** +** sup_type - (L2CAP_FCR_SUP_RR or L2CAP_FCR_SUP_RNR) +** +** Returns void +** +*******************************************************************************/ +void L2CA_SendPolledSFrame (UINT16 cid, UINT16 sup_type) +{ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, cid); + + if (p_ccb && (sup_type == L2CAP_FCR_SUP_RR || sup_type == L2CAP_FCR_SUP_RNR)) + { + l2c_fcr_send_S_frame (p_ccb, sup_type, L2CAP_FCR_P_BIT); + } + else + { + L2CAP_TRACE_ERROR2 ("L2CA_SendPolledSFrame(ERROR): sup_type %u, p_ccb 0x%07x", + sup_type, (UINT32)p_ccb); + } +} + +/******************************************************************************* +** +** Function l2c_bypass_sframe_packet +** +** Description This function is used for testing purposes only. +** It returns TRUE if S-Frame should be bypassed. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN l2c_bypass_sframe_packet (tL2C_CCB *p_ccb) +{ + if (p_ccb && p_ccb->fcrb.test_cb.cfm.in_use) + { + if (p_ccb->fcrb.test_cb.cfm.skip_sframe_count > 0) + { + L2CAP_TRACE_ERROR1 ("l2c_bypass_sframe_packet (count %d)", + p_ccb->fcrb.test_cb.cfm.skip_sframe_count); + + if (--p_ccb->fcrb.test_cb.cfm.skip_sframe_count == 0) + p_ccb->fcrb.test_cb.cfm.in_use = FALSE; + + /* Bypass sending S-Frame */ + return TRUE; + } + else + p_ccb->fcrb.test_cb.cfm.in_use = FALSE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function L2CA_BypassSFrame +** +** Description This function is used for testing purposes only. +** It skips sending 'count' S-Frames. +** +** Parameters +** cid - channel ID +** +** count - Number of S-Frames to skip sending +** +** Returns void +** +*******************************************************************************/ +void L2CA_BypassSFrame (UINT16 cid, UINT8 count) +{ + tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, cid); + tL2C_FCR_CFM_TEST_CB *p_test_cb; + + if (!p_ccb) + { + L2CAP_TRACE_WARNING1 ("L2CA_BypassSFrame(ERROR): no p_ccb (0x%07x)", (UINT32)p_ccb); + return; + } + + p_test_cb = &p_ccb->fcrb.test_cb.cfm; + + /* Initiate test if not active */ + if (!p_test_cb->in_use) + { + p_test_cb->in_use = TRUE; + p_test_cb->skip_sframe_count = count; + } + else + { + L2CAP_TRACE_WARNING0 ("L2CA_BypassSFrame(ERROR): already in use (ignoring...)"); + } +} + +#endif /* L2CAP_CORRUPT_ERTM_PKTS == TRUE */ + +#if (L2CAP_ERTM_STATS == TRUE) +/******************************************************************************* +** +** Function l2c_fcr_collect_ack_delay +** +** Description collect throughput, delay, queue size of waiting ack +** +** Parameters +** tL2C_CCB +** +** Returns void +** +*******************************************************************************/ +static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked) +{ + UINT32 index; + BT_HDR *p_buf; + UINT8 *p; + UINT32 timestamp, delay; + UINT8 xx; + UINT8 str[120]; + + index = p_ccb->fcrb.ack_delay_avg_index; + + /* update sum, max and min of waiting for ack queue size */ + p_ccb->fcrb.ack_q_count_avg[index] += p_ccb->fcrb.waiting_for_ack_q.count; + + if ( p_ccb->fcrb.waiting_for_ack_q.count > p_ccb->fcrb.ack_q_count_max[index] ) + p_ccb->fcrb.ack_q_count_max[index] = p_ccb->fcrb.waiting_for_ack_q.count; + + if ( p_ccb->fcrb.waiting_for_ack_q.count < p_ccb->fcrb.ack_q_count_min[index] ) + p_ccb->fcrb.ack_q_count_min[index] = p_ccb->fcrb.waiting_for_ack_q.count; + + /* update sum, max and min of round trip delay of acking */ + p_buf = (BT_HDR *)(p_ccb->fcrb.waiting_for_ack_q.p_first); + for (xx = 0; (xx < num_bufs_acked)&&(p_buf); xx++) + { + /* adding up length of acked I-frames to get throughput */ + p_ccb->fcrb.throughput[index] += p_buf->len - 8; + + if ( xx == num_bufs_acked - 1 ) + { + /* get timestamp from tx I-frame that receiver is acking */ + p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len; + if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) + { + p += L2CAP_FCS_LEN; + } + + STREAM_TO_UINT32 (timestamp, p); + delay = GKI_get_os_tick_count() - timestamp; + + p_ccb->fcrb.ack_delay_avg[index] += delay; + if ( delay > p_ccb->fcrb.ack_delay_max[index] ) + p_ccb->fcrb.ack_delay_max[index] = delay; + if ( delay < p_ccb->fcrb.ack_delay_min[index] ) + p_ccb->fcrb.ack_delay_min[index] = delay; + } + + p_buf = GKI_getnext(p_buf); + } + + p_ccb->fcrb.ack_delay_avg_count++; + + /* calculate average and initialize next avg, min and max */ + if (p_ccb->fcrb.ack_delay_avg_count > L2CAP_ERTM_STATS_AVG_NUM_SAMPLES) + { + p_ccb->fcrb.ack_delay_avg_count = 0; + + p_ccb->fcrb.ack_q_count_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES; + p_ccb->fcrb.ack_delay_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES; + + /* calculate throughput */ + timestamp = GKI_get_os_tick_count(); + if (timestamp - p_ccb->fcrb.throughput_start > 0 ) + p_ccb->fcrb.throughput[index] /= (timestamp - p_ccb->fcrb.throughput_start); + + p_ccb->fcrb.throughput_start = timestamp; + + sprintf(str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u", + index, p_ccb->fcrb.throughput[index], + p_ccb->fcrb.ack_delay_avg[index], p_ccb->fcrb.ack_delay_min[index], p_ccb->fcrb.ack_delay_max[index], + p_ccb->fcrb.ack_q_count_avg[index], p_ccb->fcrb.ack_q_count_min[index], p_ccb->fcrb.ack_q_count_max[index] ); + + BT_TRACE_1(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", str); + + index = (index + 1) % L2CAP_ERTM_STATS_NUM_AVG; + p_ccb->fcrb.ack_delay_avg_index = index; + + p_ccb->fcrb.ack_q_count_max[index] = 0; + p_ccb->fcrb.ack_q_count_min[index] = 0xFFFFFFFF; + p_ccb->fcrb.ack_q_count_avg[index] = 0; + + + p_ccb->fcrb.ack_delay_max[index] = 0; + p_ccb->fcrb.ack_delay_min[index] = 0xFFFFFFFF; + p_ccb->fcrb.ack_delay_avg[index] = 0; + + p_ccb->fcrb.throughput[index] = 0; + } +} +#endif + + + diff --git a/stack/l2cap/l2c_int.h b/stack/l2cap/l2c_int.h new file mode 100644 index 0000000..609c825 --- /dev/null +++ b/stack/l2cap/l2c_int.h @@ -0,0 +1,771 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP internal definitions + * + ******************************************************************************/ +#ifndef L2C_INT_H +#define L2C_INT_H + +#include "l2c_api.h" +#include "l2cdefs.h" +#include "gki.h" +#include "btm_api.h" + +#define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */ + +/* Timeouts. Since L2CAP works off a 1-second list, all are in seconds. +*/ +#define L2CAP_LINK_ROLE_SWITCH_TOUT 10 /* 10 seconds */ +#define L2CAP_LINK_CONNECT_TOUT 60 /* 30 seconds */ +#define L2CAP_LINK_CONNECT_TOUT_EXT 120 /* 120 seconds */ +#define L2CAP_ECHO_RSP_TOUT 30 /* 30 seconds */ +#define L2CAP_LINK_FLOW_CONTROL_TOUT 2 /* 2 seconds */ +#define L2CAP_LINK_DISCONNECT_TOUT 30 /* 30 seconds */ + +#ifndef L2CAP_CHNL_CONNECT_TOUT /* BTIF needs to override for internal project needs */ +#define L2CAP_CHNL_CONNECT_TOUT 60 /* 60 seconds */ +#endif + +#define L2CAP_CHNL_CONNECT_TOUT_EXT 120 /* 120 seconds */ +#define L2CAP_CHNL_CFG_TIMEOUT 30 /* 30 seconds */ +#define L2CAP_CHNL_DISCONNECT_TOUT 10 /* 10 seconds */ +#define L2CAP_DELAY_CHECK_SM4 2 /* 2 seconds */ +#define L2CAP_WAIT_INFO_RSP_TOUT 3 /* 3 seconds */ +#define L2CAP_WAIT_UNPARK_TOUT 2 /* 2 seconds */ +#define L2CAP_LINK_INFO_RESP_TOUT 2 /* 2 seconds */ +#define L2CAP_BLE_LINK_CONNECT_TOUT 30 /* 30 seconds */ + +/* quick timer uses millisecond unit */ +#define L2CAP_DEFAULT_RETRANS_TOUT 2000 /* 2000 milliseconds */ +#define L2CAP_DEFAULT_MONITOR_TOUT 12000 /* 12000 milliseconds */ +#define L2CAP_FCR_ACK_TOUT 200 /* 200 milliseconds */ + +/* Define the possible L2CAP channel states. The names of +** the states may seem a bit strange, but they are taken from +** the Bluetooth specification. +*/ +typedef enum +{ + CST_CLOSED, /* Channel is in clodes state */ + CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */ + CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */ + CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */ + CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */ + CST_CONFIG, /* Negotiating configuration */ + CST_OPEN, /* Data transfer state */ + CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */ + CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */ +} tL2C_CHNL_STATE; + +/* Define the possible L2CAP link states +*/ +typedef enum +{ + LST_DISCONNECTED, + LST_CONNECT_HOLDING, + LST_CONNECTING_WAIT_SWITCH, + LST_CONNECTING, + LST_CONNECTED, + LST_DISCONNECTING +} tL2C_LINK_STATE; + + + +/* Define input events to the L2CAP link and channel state machines. The names +** of the events may seem a bit strange, but they are taken from +** the Bluetooth specification. +*/ +#define L2CEVT_LP_CONNECT_CFM 0 /* Lower layer connect confirm */ +#define L2CEVT_LP_CONNECT_CFM_NEG 1 /* Lower layer connect confirm (failed) */ +#define L2CEVT_LP_CONNECT_IND 2 /* Lower layer connect indication */ +#define L2CEVT_LP_DISCONNECT_IND 3 /* Lower layer disconnect indication */ +#define L2CEVT_LP_QOS_CFM 4 /* Lower layer QOS confirmation */ +#define L2CEVT_LP_QOS_CFM_NEG 5 /* Lower layer QOS confirmation (failed)*/ +#define L2CEVT_LP_QOS_VIOLATION_IND 6 /* Lower layer QOS violation indication */ + +#define L2CEVT_SEC_COMP 7 /* Security cleared successfully */ +#define L2CEVT_SEC_COMP_NEG 8 /* Security procedure failed */ + +#define L2CEVT_L2CAP_CONNECT_REQ 10 /* Peer connection request */ +#define L2CEVT_L2CAP_CONNECT_RSP 11 /* Peer connection response */ +#define L2CEVT_L2CAP_CONNECT_RSP_PND 12 /* Peer connection response pending */ +#define L2CEVT_L2CAP_CONNECT_RSP_NEG 13 /* Peer connection response (failed) */ +#define L2CEVT_L2CAP_CONFIG_REQ 14 /* Peer configuration request */ +#define L2CEVT_L2CAP_CONFIG_RSP 15 /* Peer configuration response */ +#define L2CEVT_L2CAP_CONFIG_RSP_NEG 16 /* Peer configuration response (failed) */ +#define L2CEVT_L2CAP_DISCONNECT_REQ 17 /* Peer disconnect request */ +#define L2CEVT_L2CAP_DISCONNECT_RSP 18 /* Peer disconnect response */ +#define L2CEVT_L2CAP_INFO_RSP 19 /* Peer information response */ +#define L2CEVT_L2CAP_DATA 20 /* Peer data */ + +#define L2CEVT_L2CA_CONNECT_REQ 21 /* Upper layer connect request */ +#define L2CEVT_L2CA_CONNECT_RSP 22 /* Upper layer connect response */ +#define L2CEVT_L2CA_CONNECT_RSP_NEG 23 /* Upper layer connect response (failed)*/ +#define L2CEVT_L2CA_CONFIG_REQ 24 /* Upper layer config request */ +#define L2CEVT_L2CA_CONFIG_RSP 25 /* Upper layer config response */ +#define L2CEVT_L2CA_CONFIG_RSP_NEG 26 /* Upper layer config response (failed) */ +#define L2CEVT_L2CA_DISCONNECT_REQ 27 /* Upper layer disconnect request */ +#define L2CEVT_L2CA_DISCONNECT_RSP 28 /* Upper layer disconnect response */ +#define L2CEVT_L2CA_DATA_READ 29 /* Upper layer data read */ +#define L2CEVT_L2CA_DATA_WRITE 30 /* Upper layer data write */ +#define L2CEVT_L2CA_FLUSH_REQ 31 /* Upper layer flush */ + +#define L2CEVT_TIMEOUT 32 /* Timeout */ +#define L2CEVT_SEC_RE_SEND_CMD 33 /* btm_sec has enough info to proceed */ + +#define L2CEVT_ACK_TIMEOUT 34 /* RR delay timeout */ + + +/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two + successive ID values, '0' id only or both */ +#define L2CAP_ADJ_BRCM_ID 0x1 +#define L2CAP_ADJ_ZERO_ID 0x2 +#define L2CAP_ADJ_ID 0x3 + +/* Return values for l2cu_process_peer_cfg_req() */ +#define L2CAP_PEER_CFG_UNACCEPTABLE 0 +#define L2CAP_PEER_CFG_OK 1 +#define L2CAP_PEER_CFG_DISCONNECT 2 + +/* eL2CAP option constants */ +#define L2CAP_MIN_RETRANS_TOUT 2000 /* Min retransmission timeout if no flush timeout or PBF */ +#define L2CAP_MIN_MONITOR_TOUT 12000 /* Min monitor timeout if no flush timeout or PBF */ + +#define L2CAP_MAX_FCR_CFG_TRIES 2 /* Config attempts before disconnecting */ + +/* Only compiled in when in test mode. Production devices must not include +*/ +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + +/* These are used for conformance and corruption testing only */ +typedef struct +{ + BOOLEAN in_use; /* TRUE if test in progress */ + UINT8 type; /* Type of test to run or turns off random test */ + UINT8 freq; /* One-shot or random */ + BOOLEAN is_rx; /* TRUE if incoming packets */ + UINT16 count; /* How many I-frames to drop in a row; used only with one-shot tests */ +} tL2C_FCR_TEST_CFG; + +typedef struct +{ + BOOLEAN in_use; /* TRUE if test in progress */ + UINT8 skip_sframe_count; /* Number of S-Frames to skip sending */ +} tL2C_FCR_CFM_TEST_CB; + +typedef struct +{ + tL2C_FCR_TEST_CFG cfg; /* Current corruption test configuration */ + tL2C_FCR_CFM_TEST_CB cfm; /* Conformance test structure */ +} tL2C_FCR_TEST_CB; + +#endif /* L2CAP_CORRUPT_ERTM_PKTS == TRUE */ + +typedef struct +{ + UINT8 next_tx_seq; /* Next sequence number to be Tx'ed */ + UINT8 last_rx_ack; /* Last sequence number ack'ed by the peer */ + UINT8 next_seq_expected; /* Next peer sequence number expected */ + UINT8 last_ack_sent; /* Last peer sequence number ack'ed */ + UINT8 num_tries; /* Number of retries to send a packet */ + UINT8 max_held_acks; /* Max acks we can hold before sending */ + + BOOLEAN remote_busy; /* TRUE if peer has flowed us off */ + BOOLEAN local_busy; /* TRUE if we have flowed off the peer */ + + BOOLEAN rej_sent; /* Reject was sent */ + BOOLEAN srej_sent; /* Selective Reject was sent */ + BOOLEAN wait_ack; /* Transmitter is waiting ack (poll sent) */ + BOOLEAN rej_after_srej; /* Send a REJ when SREJ clears */ + + BOOLEAN send_f_rsp; /* We need to send an F-bit response */ + + UINT16 rx_sdu_len; /* Length of the SDU being received */ + BT_HDR *p_rx_sdu; /* Buffer holding the SDU being received */ + BUFFER_Q waiting_for_ack_q; /* Buffers sent and waiting for peer to ack */ + BUFFER_Q srej_rcv_hold_q; /* Buffers rcvd but held pending SREJ rsp */ + BUFFER_Q retrans_q; /* Buffers being retransmitted */ + + TIMER_LIST_ENT ack_timer; /* Timer delaying RR */ + TIMER_LIST_ENT mon_retrans_timer; /* Timer Monitor or Retransmission */ + +#if (L2CAP_ERTM_STATS == TRUE) + UINT32 connect_tick_count; /* Time channel was established */ + UINT32 ertm_pkt_counts[2]; /* Packets sent and received */ + UINT32 ertm_byte_counts[2]; /* Bytes sent and received */ + UINT32 s_frames_sent[4]; /* S-frames sent (RR, REJ, RNR, SREJ) */ + UINT32 s_frames_rcvd[4]; /* S-frames rcvd (RR, REJ, RNR, SREJ) */ + UINT32 xmit_window_closed; /* # of times the xmit window was closed */ + UINT32 controller_idle; /* # of times less than 2 packets in controller */ + /* when the xmit window was closed */ + UINT32 pkts_retransmitted; /* # of packets that were retransmitted */ + UINT32 retrans_touts; /* # of retransmission timouts */ + UINT32 xmit_ack_touts; /* # of xmit ack timouts */ + +#define L2CAP_ERTM_STATS_NUM_AVG 10 +#define L2CAP_ERTM_STATS_AVG_NUM_SAMPLES 100 + UINT32 ack_delay_avg_count; + UINT32 ack_delay_avg_index; + UINT32 throughput_start; + UINT32 throughput[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_avg[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_min[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_delay_max[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_avg[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_min[L2CAP_ERTM_STATS_NUM_AVG]; + UINT32 ack_q_count_max[L2CAP_ERTM_STATS_NUM_AVG]; +#endif + +#if L2CAP_CORRUPT_ERTM_PKTS == TRUE + tL2C_FCR_TEST_CB test_cb; /* Used for SVT and UPF testing */ +#endif + +} tL2C_FCRB; + + +/* Define a registration control block. Every application (e.g. RFCOMM, SDP, +** TCS etc) that registers with L2CAP is assigned one of these. +*/ +#if (L2CAP_UCD_INCLUDED == TRUE) +#define L2C_UCD_RCB_ID 0x00 +#define L2C_UCD_STATE_UNUSED 0x00 +#define L2C_UCD_STATE_W4_DATA 0x01 +#define L2C_UCD_STATE_W4_RECEPTION 0x02 +#define L2C_UCD_STATE_W4_MTU 0x04 + +typedef struct +{ + UINT8 state; + tL2CAP_UCD_CB_INFO cb_info; +} tL2C_UCD_REG; +#endif + +typedef struct +{ + BOOLEAN in_use; + UINT16 psm; + UINT16 real_psm; /* This may be a dummy RCB for an o/b connection but */ + /* this is the real PSM that we need to connect to */ +#if (L2CAP_UCD_INCLUDED == TRUE) + tL2C_UCD_REG ucd; +#endif + + tL2CAP_APPL_INFO api; +} tL2C_RCB; + + +/* Define a channel control block (CCB). There may be many channel control blocks +** between the same two Bluetooth devices (i.e. on the same link). +** Each CCB has unique local and remote CIDs. All channel control blocks on +** the same physical link and are chained together. +*/ +typedef struct t_l2c_ccb +{ + BOOLEAN in_use; /* TRUE when in use, FALSE when not */ + tL2C_CHNL_STATE chnl_state; /* Channel state */ + + struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */ + struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */ + struct t_l2c_linkcb *p_lcb; /* Link this CCB is assigned to */ + + UINT16 local_cid; /* Local CID */ + UINT16 remote_cid; /* Remote CID */ + + TIMER_LIST_ENT timer_entry; /* CCB Timer List Entry */ + + tL2C_RCB *p_rcb; /* Registration CB for this Channel */ + +#define IB_CFG_DONE 0x01 +#define OB_CFG_DONE 0x02 +#define RECONFIG_FLAG 0x04 /* True after initial configuration */ +#define CFG_DONE_MASK (IB_CFG_DONE | OB_CFG_DONE) + + UINT8 config_done; /* Configuration flag word */ + UINT8 local_id; /* Transaction ID for local trans */ + UINT8 remote_id; /* Transaction ID for local */ + +#define CCB_FLAG_NO_RETRY 0x01 /* no more retry */ +#define CCB_FLAG_SENT_PENDING 0x02 /* already sent pending response */ + UINT8 flags; + + tL2CAP_CFG_INFO our_cfg; /* Our saved configuration options */ + tL2CAP_CH_CFG_BITS peer_cfg_bits; /* Store what peer wants to configure */ + tL2CAP_CFG_INFO peer_cfg; /* Peer's saved configuration options */ + + BUFFER_Q xmit_hold_q; /* Transmit data hold queue */ + + BOOLEAN cong_sent; /* Set when congested status sent */ + UINT16 buff_quota; /* Buffer quota before sending congestion */ + + tL2CAP_CHNL_PRIORITY ccb_priority; /* Channel priority */ + tL2CAP_CHNL_DATA_RATE tx_data_rate; /* Channel Tx data rate */ + tL2CAP_CHNL_DATA_RATE rx_data_rate; /* Channel Rx data rate */ + + /* Fields used for eL2CAP */ + tL2CAP_ERTM_INFO ertm_info; + tL2C_FCRB fcrb; + UINT16 tx_mps; /* TX MPS adjusted based on current controller */ + UINT16 max_rx_mtu; + UINT8 fcr_cfg_tries; /* Max number of negotiation attempts */ + BOOLEAN peer_cfg_already_rejected; /* If mode rejected once, set to TRUE */ + BOOLEAN out_cfg_fcr_present; /* TRUE if cfg response shoulkd include fcr options */ + +#define L2CAP_CFG_FCS_OUR 0x01 /* Our desired config FCS option */ +#define L2CAP_CFG_FCS_PEER 0x02 /* Peer's desired config FCS option */ +#define L2CAP_BYPASS_FCS (L2CAP_CFG_FCS_OUR | L2CAP_CFG_FCS_PEER) + UINT8 bypass_fcs; + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + BOOLEAN is_flushable; /* TRUE if channel is flushable */ +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) || (L2CAP_UCD_INCLUDED == TRUE) + UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */ +#endif + +} tL2C_CCB; + +/*********************************************************************** +** Define a queue of linked CCBs. +*/ +typedef struct +{ + tL2C_CCB *p_first_ccb; /* The first channel in this queue */ + tL2C_CCB *p_last_ccb; /* The last channel in this queue */ +} tL2C_CCB_Q; + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + +/* Round-Robin service for the same priority channels */ +#define L2CAP_NUM_CHNL_PRIORITY 3 /* Total number of priority group (high, medium, low)*/ +#define L2CAP_CHNL_PRIORITY_WEIGHT 5 /* weight per priority for burst transmission quota */ +#define L2CAP_GET_PRIORITY_QUOTA(pri) ((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT) + +/* CCBs within the same LCB are served in round robin with priority */ +/* It will make sure that low priority channel (for example, HF signaling on RFCOMM) */ +/* can be sent to headset even if higher priority channel (for example, AV media channel) */ +/* is congested. */ + +typedef struct +{ + tL2C_CCB *p_serve_ccb; /* current serving ccb within priority group */ + tL2C_CCB *p_first_ccb; /* first ccb of priority group */ + UINT8 num_ccb; /* number of channels in priority group */ + UINT8 quota; /* burst transmission quota */ +} tL2C_RR_SERV; + +#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + +/* Define a link control block. There is one link control block between +** this device and any other device (i.e. BD ADDR). +*/ +typedef struct t_l2c_linkcb +{ + BOOLEAN in_use; /* TRUE when in use, FALSE when not */ + tL2C_LINK_STATE link_state; + + TIMER_LIST_ENT timer_entry; /* Timer list entry for timeout evt */ + UINT16 handle; /* The handle used with LM */ + + tL2C_CCB_Q ccb_queue; /* Queue of CCBs on this LCB */ + + tL2C_CCB *p_pending_ccb; /* ccb of waiting channel during link disconnect */ + TIMER_LIST_ENT info_timer_entry; /* Timer entry for info resp timeout evt */ + BD_ADDR remote_bd_addr; /* The BD address of the remote */ + + UINT8 link_role; /* Master or slave */ + UINT8 id; + tL2CA_ECHO_RSP_CB *p_echo_rsp_cb; /* Echo response callback */ + UINT16 idle_timeout; /* Idle timeout */ + BOOLEAN is_bonding; /* True - link active only for bonding */ + + UINT16 link_flush_tout; /* Flush timeout used */ + + UINT16 link_xmit_quota; /* Num outstanding pkts allowed */ + UINT16 sent_not_acked; /* Num packets sent but not acked */ + + BOOLEAN partial_segment_being_sent; /* Set TRUE when a partial segment */ + /* is being sent. */ + BOOLEAN w4_info_rsp; /* TRUE when info request is active */ + UINT8 info_rx_bits; /* set 1 if received info type */ + UINT32 peer_ext_fea; /* Peer's extended features mask */ + BUFFER_Q link_xmit_data_q; /* Transmit data buffer queue */ + + UINT8 peer_chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE]; +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT16 ucd_mtu; /* peer MTU on UCD */ + BUFFER_Q ucd_out_sec_pending_q; /* Security pending outgoing UCD packet */ + BUFFER_Q ucd_in_sec_pending_q; /* Security pending incoming UCD packet */ +#endif + +#if (L2CAP_HOST_FLOW_CTRL == TRUE) + UINT16 link_pkts_unacked; /* Packets received but not acked */ + UINT16 link_ack_thresh; /* Threshold at which to ack pkts */ +#endif + + BT_HDR *p_hcit_rcv_acl; /* Current HCIT ACL buf being rcvd */ + UINT16 idle_timeout_sv; /* Save current Idle timeout */ + UINT8 acl_priority; /* L2C_PRIORITY_NORMAL or L2C_PRIORITY_HIGH */ + tL2CA_NOCP_CB *p_nocp_cb; /* Num Cmpl pkts callback */ + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2C_CCB *p_fixed_ccbs[L2CAP_NUM_FIXED_CHNLS]; + UINT16 disc_reason; +#endif + +#if (BLE_INCLUDED == TRUE) + BOOLEAN is_ble_link; + tBLE_ADDR_TYPE ble_addr_type; + +#define UPD_ENABLED 0 /* If peer requests update, we will change params */ +#define UPD_DISABLED 1 /* application requested not to update */ +#define UPD_PENDING 2 /* while updates are disabled, peer requested new parameters */ +#define UPD_UPDATED 3 /* peer updated connection parameters */ + UINT8 upd_disabled; + + UINT16 min_interval; /* parameters as requested by peripheral */ + UINT16 max_interval; + UINT16 latency; + UINT16 timeout; + +#endif + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* each priority group is limited burst transmission */ + /* round robin service for the same priority channels */ + tL2C_RR_SERV rr_serv[L2CAP_NUM_CHNL_PRIORITY]; + UINT8 rr_pri; /* current serving priority group */ +#endif + +} tL2C_LCB; + +/* Define the L2CAP control structure +*/ +typedef struct +{ + UINT8 l2cap_trace_level; + UINT16 controller_xmit_window; /* Total ACL window for all links */ + + UINT16 round_robin_quota; /* Round-robin link quota */ + UINT16 round_robin_unacked; /* Round-robin unacked */ + BOOLEAN check_round_robin; /* Do a round robin check */ + + BOOLEAN is_cong_cback_context; + + tL2C_LCB lcb_pool[MAX_L2CAP_LINKS]; /* Link Control Block pool */ + tL2C_CCB ccb_pool[MAX_L2CAP_CHANNELS]; /* Channel Control Block pool */ + tL2C_RCB rcb_pool[MAX_L2CAP_CLIENTS]; /* Registration info pool */ + + tL2C_CCB *p_free_ccb_first; /* Pointer to first free CCB */ + tL2C_CCB *p_free_ccb_last; /* Pointer to last free CCB */ + + UINT8 desire_role; /* desire to be master/slave when accepting a connection */ + BOOLEAN disallow_switch; /* FALSE, to allow switch at create conn */ + UINT16 num_lm_acl_bufs; /* # of ACL buffers on controller */ + UINT16 idle_timeout; /* Idle timeout */ + + BUFFER_Q rcv_hold_q; /* Recv pending queue */ + TIMER_LIST_ENT rcv_hold_tle; /* Timer list entry for rcv hold */ + + tL2C_LCB *p_cur_hcit_lcb; /* Current HCI Transport buffer */ + UINT16 num_links_active; /* Number of links active */ + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + UINT16 non_flushable_pbf; /* L2CAP_PKT_START_NON_FLUSHABLE if controller supports */ + /* Otherwise, L2CAP_PKT_START */ + BOOLEAN is_flush_active; /* TRUE if an HCI_Enhanced_Flush has been sent */ +#endif + +#if L2CAP_CONFORMANCE_TESTING == TRUE + UINT32 test_info_resp; /* Conformance testing needs a dynamic response */ +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2CAP_FIXED_CHNL_REG fixed_reg[L2CAP_NUM_FIXED_CHNLS]; /* Reg info for fixed channels */ +#endif + +#if (BLE_INCLUDED == TRUE) + BOOLEAN is_ble_connecting; + BD_ADDR ble_connecting_bda; + UINT16 controller_le_xmit_window; /* Total ACL window for all links */ + UINT16 num_lm_ble_bufs; /* # of ACL buffers on controller */ +#endif + + tL2CA_ECHO_DATA_CB *p_echo_data_cb; /* Echo data callback */ + +#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE)) + UINT16 high_pri_min_xmit_quota; /* Minimum number of ACL credit for high priority link */ +#endif /* (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE) */ + + UINT16 dyn_psm; +} tL2C_CB; + + + +/* Define a structure that contains the information about a connection. +** This structure is used to pass between functions, and not all the +** fields will always be filled in. +*/ +typedef struct +{ + BD_ADDR bd_addr; /* Remote BD address */ + UINT8 status; /* Connection status */ + UINT16 psm; /* PSM of the connection */ + UINT16 l2cap_result; /* L2CAP result */ + UINT16 l2cap_status; /* L2CAP status */ + UINT16 remote_cid; /* Remote CID */ +} tL2C_CONN_INFO; + + +typedef void (tL2C_FCR_MGMT_EVT_HDLR) (UINT8, tL2C_CCB *); + +/* The offset in a buffer that L2CAP will use when building commands. +*/ +#define L2CAP_SEND_CMD_OFFSET 0 + + +/* Number of ACL buffers to use for high priority channel +*/ +#if (!defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) || (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == FALSE)) +#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (L2CAP_HIGH_PRI_MIN_XMIT_QUOTA) +#else +#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (l2cb.high_pri_min_xmit_quota) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* L2CAP global data +************************************ +*/ +#if (!defined L2C_DYNAMIC_MEMORY) || (L2C_DYNAMIC_MEMORY == FALSE) +L2C_API extern tL2C_CB l2cb; +#else +L2C_API extern tL2C_CB *l2c_cb_ptr; +#define l2cb (*l2c_cb_ptr) +#endif + + +/* Functions provided by l2c_main.c +************************************ +*/ +extern void l2c_init (void); +extern void l2c_process_timeout (TIMER_LIST_ENT *p_tle); +extern UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flag); +extern void l2c_rcv_acl_data (BT_HDR *p_msg); +extern void l2c_process_held_packets (BOOLEAN timed_out); + +/* Functions provided by l2c_utils.c +************************************ +*/ +extern tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding); +extern BOOLEAN l2cu_start_post_bond_timer (UINT16 handle); +extern void l2cu_release_lcb (tL2C_LCB *p_lcb); +extern tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr); +extern tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle); +extern void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding); + +extern UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb); +extern BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs); + +extern void l2cu_enqueue_ccb (tL2C_CCB *p_ccb); +extern void l2cu_dequeue_ccb (tL2C_CCB *p_ccb); +extern void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority); + +extern tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid); +extern void l2cu_release_ccb (tL2C_CCB *p_ccb); +extern tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid); +extern tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid); +extern void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask); + +extern void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, + UINT8 rem_id,UINT16 p1, UINT16 p2); +extern void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status); +extern void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len); +extern void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb); +extern void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, UINT16 remote_cid); +extern void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT16 info_type); +extern void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result); +extern void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type); +extern void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb); +extern void l2cu_check_channel_congestion (tL2C_CCB *p_ccb); +extern void l2cu_disconnect_chnl (tL2C_CCB *p_ccb); + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +extern void l2cu_set_non_flushable_pbf(BOOLEAN); +#endif + +#if (BLE_INCLUDED == TRUE) +extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); +extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id); +#endif + +extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr); +extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb); +extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb); + +/* Functions provided by l2c_ucd.c +************************************ +*/ +#if (L2CAP_UCD_INCLUDED == TRUE) +void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb); +void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data); +BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb); +void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb); +BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg); +BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data); +#endif + +#if (BLE_INCLUDED == TRUE) +extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout); +extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id); +#endif + +extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr); +extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb); +extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb); + + +/* Functions provided for Broadcom Aware +**************************************** +*/ +extern BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len); +extern void l2cu_send_feature_req (tL2C_CCB *p_ccb); + +extern tL2C_RCB *l2cu_allocate_rcb (UINT16 psm); +extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm); +extern void l2cu_release_rcb (tL2C_RCB *p_rcb); + +extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); + +extern void l2cu_device_reset (void); +extern tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state); +extern BOOLEAN l2cu_lcb_disconnecting (void); + +extern BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb); +extern BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb); +extern BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb); +extern void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda); +extern void l2cu_initialize_amp_ccb (tL2C_LCB *p_lcb); +extern void l2cu_adjust_out_mps (tL2C_CCB *p_ccb); + +/* Functions provided by l2c_link.c +************************************ +*/ +extern BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr); +extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda); +extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason); +extern BOOLEAN l2c_link_hci_qos_violation (UINT16 handle); +extern void l2c_link_timeout (tL2C_LCB *p_lcb); +extern void l2c_info_timeout (tL2C_LCB *p_lcb); +extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf); +extern void l2c_link_adjust_allocation (void); +extern void l2c_link_process_num_completed_pkts (UINT8 *p); +extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len); +extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs); +extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles); +extern void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status); +extern void l2c_link_sec_comp (BD_ADDR p_bda, void *p_ref_data, UINT8 status); +extern void l2c_link_segments_xmitted (BT_HDR *p_msg); +extern void l2c_pin_code_request (BD_ADDR bd_addr); +extern void l2c_link_adjust_chnl_allocation (void); + +#if (BLE_INCLUDED == TRUE) +extern void l2c_link_processs_ble_num_bufs (UINT16 num_lm_acl_bufs); +#endif + +#if ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE) +extern BOOLEAN l2c_link_check_power_mode ( tL2C_LCB *p_lcb ); +#define L2C_LINK_CHECK_POWER_MODE(x) l2c_link_check_power_mode ((x)) +#else +#define L2C_LINK_CHECK_POWER_MODE(x) (FALSE) +#endif + +#if L2CAP_CONFORMANCE_TESTING == TRUE +/* Used only for conformance testing */ +L2C_API extern void l2cu_set_info_rsp_mask (UINT32 mask); +#endif + +/* Functions provided by l2c_csm.c +************************************ +*/ +extern void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data); + +L2C_API extern BT_HDR *l2cap_link_chk_pkt_start(BT_HDR *); /* Called at start of rcv to check L2CAP segmentation */ +L2C_API extern BOOLEAN l2cap_link_chk_pkt_end (void); /* Called at end of rcv to check L2CAP segmentation */ + +L2C_API extern void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf); + + +/* Functions provided by l2c_fcr.c +************************************ +*/ +extern void l2c_fcr_cleanup (tL2C_CCB *p_ccb); +extern void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf); +extern void l2c_fcr_proc_tout (tL2C_CCB *p_ccb); +extern void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb); +extern void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit); +extern BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes, UINT8 pool); +extern BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb); +extern BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length); +extern void l2c_fcr_start_timer (tL2C_CCB *p_ccb); + +/* Configuration negotiation */ +extern UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb); +extern BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_peer_cfg); +extern BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg); +extern void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb); +extern void l2c_fcr_stop_timer (tL2C_CCB *p_ccb); + +/* Functions provided by l2c_ble.c +************************************ +*/ +#if (BLE_INCLUDED == TRUE) +extern BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb); +extern void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len); +extern void l2cble_conn_comp (UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type, + UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/l2cap/l2c_link.c b/stack/l2cap/l2c_link.c new file mode 100644 index 0000000..401084a --- /dev/null +++ b/stack/l2cap/l2c_link.c @@ -0,0 +1,1719 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the functions relating to link management. A "link" + * is a connection between this device and another device. Only ACL links + * are managed. + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "l2c_api.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" + +static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf); + +#define L2C_LINK_SEND_ACL_DATA(x) HCI_ACL_DATA_TO_LOWER((x)) + +#if (BLE_INCLUDED == TRUE) +#define L2C_LINK_SEND_BLE_ACL_DATA(x) HCI_BLE_ACL_DATA_TO_LOWER((x)) +#endif + +/******************************************************************************* +** +** Function l2c_link_hci_conn_req +** +** Description This function is called when an HCI Connection Request +** event is received. +** +** Returns TRUE, if accept conn +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb; + tL2C_LCB *p_lcb_cur; + int xx; + BOOLEAN no_links; + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr); + + /* If we don't have one, create one and accept the connection. */ + if (!p_lcb) + { + p_lcb = l2cu_allocate_lcb (bd_addr, FALSE); + if (!p_lcb) + { + btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_RESOURCES); + L2CAP_TRACE_ERROR0 ("L2CAP failed to allocate LCB"); + return FALSE; + } + + no_links = TRUE; + + /* If we already have connection, accept as a master */ + for (xx = 0, p_lcb_cur = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb_cur++) + { + if (p_lcb_cur == p_lcb) + continue; + + if (p_lcb_cur->in_use) + { + no_links = FALSE; + p_lcb->link_role = HCI_ROLE_MASTER; + break; + } + } + + if (no_links) + { + if (!btm_dev_support_switch (bd_addr)) + p_lcb->link_role = HCI_ROLE_SLAVE; + else + p_lcb->link_role = l2cu_get_conn_role(p_lcb); + } + + /* Tell the other side we accept the connection */ + btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role); + + p_lcb->link_state = LST_CONNECTING; + + /* Start a timer waiting for connect complete */ + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT); + return (TRUE); + } + + /* We already had a link control block to the guy. Check what state it is in */ + if ((p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_CONNECT_HOLDING)) + { + /* Connection collision. Accept the connection anyways. */ + + if (!btm_dev_support_switch (bd_addr)) + p_lcb->link_role = HCI_ROLE_SLAVE; + else + p_lcb->link_role = l2cu_get_conn_role(p_lcb); + + btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role); + + p_lcb->link_state = LST_CONNECTING; + return (TRUE); + } + else if (p_lcb->link_state == LST_DISCONNECTING) + { + /* In disconnecting state, reject the connection. */ + btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_DEVICE); + } + else + { + L2CAP_TRACE_ERROR0 ("L2CAP got conn_req while connected"); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_link_hci_conn_comp +** +** Description This function is called when an HCI Connection Complete +** event is received. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda) +{ + tL2C_CONN_INFO ci; + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tBTM_SEC_DEV_REC *p_dev_info = NULL; + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_PAGE_DONE_EVT); +#endif + + /* Save the parameters */ + ci.status = status; + memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN); + + /* See if we have a link control block for the remote device */ + p_lcb = l2cu_find_lcb_by_bd_addr (ci.bd_addr); + + /* If we don't have one, this is an error */ + if (!p_lcb) + { + L2CAP_TRACE_WARNING0 ("L2CAP got conn_comp for unknown BD_ADDR"); + return (FALSE); + } + + if (p_lcb->link_state != LST_CONNECTING) + { + L2CAP_TRACE_ERROR2 ("L2CAP got conn_comp in bad state: %d status: 0x%d", p_lcb->link_state, status); + + if (status != HCI_SUCCESS) + l2c_link_hci_disc_comp (p_lcb->handle, status); + + return (FALSE); + } + + /* Save the handle */ + p_lcb->handle = handle; + + if (ci.status == HCI_SUCCESS) + { + /* Connected OK. Change state to connected */ + p_lcb->link_state = LST_CONNECTED; + + /* Get the peer information if the l2cap flow-control/rtrans is supported */ + l2cu_send_peer_info_req (p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE); + + /* Tell BTM Acl management about the link */ + if ((p_dev_info = btm_find_dev (p_bda)) != NULL) + btm_acl_created (ci.bd_addr, p_dev_info->dev_class, + p_dev_info->sec_bd_name, handle, + p_lcb->link_role, FALSE); + else + btm_acl_created (ci.bd_addr, NULL, NULL, handle, p_lcb->link_role, FALSE); + + /* If dedicated bonding do not process any further */ + if (p_lcb->is_bonding) + { + if (l2cu_start_post_bond_timer(handle)) + return (TRUE); + } + + /* Update the timeouts in the hold queue */ + l2c_process_held_packets(FALSE); + + btu_stop_timer (&p_lcb->timer_entry); + + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, &ci); + } + + if (p_lcb->p_echo_rsp_cb) + { + l2cu_send_peer_echo_req (p_lcb, NULL, 0); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT); + } + else if (!p_lcb->ccb_queue.p_first_ccb) + { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_STARTUP_TOUT); + } + } + /* Max number of acl connections. */ + /* If there's an lcb disconnecting set this one to holding */ + else if ((ci.status == HCI_ERR_MAX_NUM_OF_CONNECTIONS) && l2cu_lcb_disconnecting()) + { + p_lcb->link_state = LST_CONNECT_HOLDING; + p_lcb->handle = HCI_INVALID_HANDLE; + } + else + { + /* Just in case app decides to try again in the callback context */ + p_lcb->link_state = LST_DISCONNECTING; + + /* Connection failed. For all channels, send the event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) + { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, &ci); + + p_ccb = pn; + } + + p_lcb->disc_reason = status; + /* Release the LCB */ + if (p_lcb->ccb_queue.p_first_ccb == NULL) + l2cu_release_lcb (p_lcb); + else /* there are any CCBs remaining */ + { + if (ci.status == HCI_ERR_CONNECTION_EXISTS) + { + /* we are in collision situation, wait for connecttion request from controller */ + p_lcb->link_state = LST_CONNECTING; + } + else + { + l2cu_create_conn(p_lcb); + } + } + } + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2c_link_sec_comp +** +** Description This function is called when required security procedures +** are completed. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_sec_comp (BD_ADDR p_bda, void *p_ref_data, UINT8 status) +{ + tL2C_CONN_INFO ci; + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_CCB *p_next_ccb; + UINT8 event; + + L2CAP_TRACE_DEBUG2 ("l2c_link_sec_comp: %d, 0x%x", status, p_ref_data); + + if (status == BTM_SUCCESS_NO_SECURITY) + status = BTM_SUCCESS; + + /* Save the parameters */ + ci.status = status; + memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN); + + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda); + + /* If we don't have one, this is an error */ + if (!p_lcb) + { + L2CAP_TRACE_WARNING0 ("L2CAP got sec_comp for unknown BD_ADDR"); + return; + } + + /* Match p_ccb with p_ref_data returned by sec manager */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) + { + p_next_ccb = p_ccb->p_next_ccb; + + if (p_ccb == p_ref_data) + { + switch(status) + { + case BTM_SUCCESS: + L2CAP_TRACE_DEBUG1 ("ccb timer ticks: %u", p_ccb->timer_entry.ticks); + event = L2CEVT_SEC_COMP; + break; + + case BTM_DELAY_CHECK: + /* start a timer - encryption change not received before L2CAP connect req */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4); + return; + + default: + event = L2CEVT_SEC_COMP_NEG; + } + l2c_csm_execute (p_ccb, event, &ci); + break; + } + } +} + + +/******************************************************************************* +** +** Function l2c_link_hci_disc_comp +** +** Description This function is called when an HCI Disconnect Complete +** event is received. +** +** Returns TRUE if the link is known about, else FALSE +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + BOOLEAN status = TRUE; + BOOLEAN lcb_is_free = TRUE; + + /* See if we have a link control block for the connection */ + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* If we don't have one, maybe an SCO link. Send to MM */ + if (!p_lcb) + { + status = FALSE; + } + else + { + /* There can be a case when we rejected PIN code authentication */ + /* otherwise save a new reason */ + if (btm_cb.acl_disc_reason != HCI_ERR_HOST_REJECT_SECURITY) + btm_cb.acl_disc_reason = reason; + + p_lcb->disc_reason = btm_cb.acl_disc_reason; + + /* Just in case app decides to try again in the callback context */ + p_lcb->link_state = LST_DISCONNECTING; + + /* Link is disconnected. For all channels, send the event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) + { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + /* Keep connect pending control block (if exists) + * Possible Race condition when a reconnect occurs + * on the channel during a disconnect of link. This + * ccb will be automatically retried after link disconnect + * arrives + */ + if (p_ccb != p_lcb->p_pending_ccb) + { + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason); + } + p_ccb = pn; + } + +#if BTM_SCO_INCLUDED == TRUE + /* Tell SCO management to drop any SCOs on this ACL */ + btm_sco_acl_removed (p_lcb->remote_bd_addr); +#endif + + /* If waiting for disconnect and reconnect is pending start the reconnect now + race condition where layer above issued connect request on link that was + disconnecting + */ + if (p_lcb->ccb_queue.p_first_ccb != NULL) + { +#if (L2CAP_NUM_FIXED_CHNLS > 0) + /* If we are going to re-use the LCB without dropping it, release all fixed channels here */ + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + { + if (p_lcb->p_fixed_ccbs[xx]) + { + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason); + l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]); + + p_lcb->p_fixed_ccbs[xx] = NULL; + } + } +#endif + L2CAP_TRACE_DEBUG0("l2c_link_hci_disc_comp: Restarting pending ACL request"); + + if (l2cu_create_conn(p_lcb)) + lcb_is_free = FALSE; /* still using this lcb */ + } + + p_lcb->p_pending_ccb = NULL; + + /* Release the LCB */ + if (lcb_is_free) + l2cu_release_lcb (p_lcb); + } + + /* Now that we have a free acl connection, see if any lcbs are pending */ + if (lcb_is_free && ((p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING)) != NULL)) + { + /* we found one-- create a connection */ + l2cu_create_conn(p_lcb); + } + + return status; +} + + +/******************************************************************************* +** +** Function l2c_link_hci_qos_violation +** +** Description This function is called when an HCI QOS Violation +** event is received. +** +** Returns TRUE if the link is known about, else FALSE +** +*******************************************************************************/ +BOOLEAN l2c_link_hci_qos_violation (UINT16 handle) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + /* See if we have a link control block for the connection */ + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* If we don't have one, maybe an SCO link. */ + if (!p_lcb) + return (FALSE); + + /* For all channels, tell the upper layer about it */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) + l2c_csm_execute (p_ccb, L2CEVT_LP_QOS_VIOLATION_IND, NULL); + } + + return (TRUE); +} + + + +/******************************************************************************* +** +** Function l2c_link_timeout +** +** Description This function is called when a link timer expires +** +** Returns void +** +*******************************************************************************/ +void l2c_link_timeout (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + UINT16 timeout; + tBTM_STATUS rc; + + L2CAP_TRACE_EVENT3 ("L2CAP - l2c_link_timeout() link state %d first CCB %p is_bonding:%d", + p_lcb->link_state, p_lcb->ccb_queue.p_first_ccb, p_lcb->is_bonding); + + /* If link was connecting or disconnecting, clear all channels and drop the LCB */ + if ((p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH) || + (p_lcb->link_state == LST_CONNECTING) || + (p_lcb->link_state == LST_CONNECT_HOLDING) || + (p_lcb->link_state == LST_DISCONNECTING)) + { + p_lcb->p_pending_ccb = NULL; + + /* For all channels, send a disconnect indication event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) + { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + + p_ccb = pn; + } +#if (BLE_INCLUDED == TRUE) + if (p_lcb->link_state == LST_CONNECTING && + l2cb.is_ble_connecting == TRUE) + { + L2CA_CancelBleConnectReq(l2cb.ble_connecting_bda); + } +#endif + /* Release the LCB */ + l2cu_release_lcb (p_lcb); + } + + /* If link is connected, check for inactivity timeout */ + if (p_lcb->link_state == LST_CONNECTED) + { + /* Check for ping outstanding */ + if (p_lcb->p_echo_rsp_cb) + { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_NO_RESP); + + L2CAP_TRACE_WARNING0 ("L2CAP - ping timeout"); + + /* For all channels, send a disconnect indication event through */ + /* their FSMs. The CCBs should remove themselves from the LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) + { + tL2C_CCB *pn = p_ccb->p_next_ccb; + + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + + p_ccb = pn; + } + } + + /* If no channels in use, drop the link. */ + if (!p_lcb->ccb_queue.p_first_ccb) + { + rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER); + + if (rc == BTM_CMD_STORED) + { + /* Security Manager will take care of disconnecting, state will be updated at that time */ + timeout = 0xFFFF; + } + else if (rc == BTM_CMD_STARTED) + { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } + else if (rc == BTM_SUCCESS) + { + /* BTM SEC will make sure that link is release (probably after pairing is done) */ + p_lcb->link_state = LST_DISCONNECTING; + timeout = 0xFFFF; + } + else if (rc == BTM_BUSY) + { + /* BTM is still executing security process. Let lcb stay as connected */ + timeout = 0xFFFF; + } + else if ((p_lcb->is_bonding) + && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER))) + { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } + else + { + /* probably no buffer to send disconnect */ + timeout = BT_1SEC_TIMEOUT; + } + + if (timeout != 0xFFFF) + { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + } + } + else + { + /* Check in case we were flow controlled */ + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + } + } +} + +/******************************************************************************* +** +** Function l2c_info_timeout +** +** Description This function is called when an info request times out +** +** Returns void +** +*******************************************************************************/ +void l2c_info_timeout (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + tL2C_CONN_INFO ci; + + /* If we timed out waiting for info response, just continue using basic if allowed */ + if (p_lcb->w4_info_rsp) + { + /* If waiting for security complete, restart the info response timer */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + if ( (p_ccb->chnl_state == CST_ORIG_W4_SEC_COMP) || (p_ccb->chnl_state == CST_TERM_W4_SEC_COMP) ) + { + btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT); + return; + } + } + + p_lcb->w4_info_rsp = FALSE; + + /* If link is in process of being brought up */ + if ((p_lcb->link_state != LST_DISCONNECTED) && + (p_lcb->link_state != LST_DISCONNECTING)) + { + /* Notify active channels that peer info is finished */ + if (p_lcb->ccb_queue.p_first_ccb) + { + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + } + } + } +} + +/******************************************************************************* +** +** Function l2c_link_adjust_allocation +** +** Description This function is called when a link is created or removed +** to calculate the amount of packets each link may send to +** the HCI without an ack coming back. +** +** Currently, this is a simple allocation, dividing the +** number of Controller Packets by the number of links. In +** the future, QOS configuration should be examined. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_adjust_allocation (void) +{ + UINT16 qq, yy, qq_remainder; + tL2C_LCB *p_lcb; + UINT16 hi_quota, low_quota; + UINT16 num_lowpri_links = 0; + UINT16 num_hipri_links = 0; + UINT16 controller_xmit_quota = l2cb.num_lm_acl_bufs; + UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A; + + /* If no links active, nothing to do. */ + if (l2cb.num_links_active == 0) + { + l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs; + l2cb.round_robin_quota = l2cb.round_robin_unacked = 0; + return; + } + + /* First, count the links */ + for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++) + { + if (p_lcb->in_use) + { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) + num_hipri_links++; + else + num_lowpri_links++; + } + } + + /* now adjust high priority link quota */ + low_quota = num_lowpri_links ? 1 : 0; + while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota ) + high_pri_link_quota--; + + /* Work out the xmit quota and buffer quota high and low priorities */ + hi_quota = num_hipri_links * high_pri_link_quota; + low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1; + + /* Work out and save the HCI xmit quota for each low priority link */ + + /* If each low priority link cannot have at least one buffer */ + if (num_lowpri_links > low_quota) + { + l2cb.round_robin_quota = low_quota; + qq = qq_remainder = 0; + } + /* If each low priority link can have at least one buffer */ + else if (num_lowpri_links > 0) + { + l2cb.round_robin_quota = 0; + l2cb.round_robin_unacked = 0; + qq = low_quota / num_lowpri_links; + qq_remainder = low_quota % num_lowpri_links; + } + /* If no low priority link */ + else + { + l2cb.round_robin_quota = 0; + l2cb.round_robin_unacked = 0; + qq = qq_remainder = 0; + } + + L2CAP_TRACE_EVENT5 ("l2c_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u", + num_hipri_links, num_lowpri_links, low_quota, + l2cb.round_robin_quota, qq); + + /* Now, assign the quotas to each link */ + for (yy = 0, p_lcb = &l2cb.lcb_pool[0]; yy < MAX_L2CAP_LINKS; yy++, p_lcb++) + { + if (p_lcb->in_use) + { + if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) + { + p_lcb->link_xmit_quota = high_pri_link_quota; + } + else + { + /* Safety check in case we switched to round-robin with something outstanding */ + /* if sent_not_acked is added into round_robin_unacked then don't add it again */ + /* l2cap keeps updating sent_not_acked for exiting from round robin */ + if (( p_lcb->link_xmit_quota > 0 )&&( qq == 0 )) + l2cb.round_robin_unacked += p_lcb->sent_not_acked; + + p_lcb->link_xmit_quota = qq; + if (qq_remainder > 0) + { + p_lcb->link_xmit_quota++; + qq_remainder--; + } + } + +#if L2CAP_HOST_FLOW_CTRL + p_lcb->link_ack_thresh = L2CAP_HOST_FC_ACL_BUFS / l2cb.num_links_active; +#endif + L2CAP_TRACE_EVENT3 ("l2c_link_adjust_allocation LCB %d Priority: %d XmitQuota: %d", + yy, p_lcb->acl_priority, p_lcb->link_xmit_quota); + + L2CAP_TRACE_EVENT2 (" SentNotAcked: %d RRUnacked: %d", + p_lcb->sent_not_acked, l2cb.round_robin_unacked); + + /* There is a special case where we have readjusted the link quotas and */ + /* this link may have sent anything but some other link sent packets so */ + /* so we may need a timer to kick off this link's transmissions. */ + if ( (p_lcb->link_state == LST_CONNECTED) + && (p_lcb->link_xmit_data_q.count) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); + } + } + +} + +/******************************************************************************* +** +** Function l2c_link_adjust_chnl_allocation +** +** Description This function is called to calculate the amount of packets each +** non-F&EC channel may have outstanding. +** +** Currently, this is a simple allocation, dividing the number +** of packets allocated to the link by the number of channels. In +** the future, QOS configuration should be examined. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_adjust_chnl_allocation (void) +{ + tL2C_CCB *p_ccb; + UINT8 xx; + + UINT16 weighted_chnls[GKI_NUM_TOTAL_BUF_POOLS]; + UINT16 quota_per_weighted_chnls[GKI_NUM_TOTAL_BUF_POOLS]; + UINT16 reserved_buff[GKI_NUM_TOTAL_BUF_POOLS]; + + L2CAP_TRACE_DEBUG0 ("l2c_link_adjust_chnl_allocation"); + + /* initialize variables */ + for (xx = 0; xx < GKI_NUM_TOTAL_BUF_POOLS; xx++ ) + { + weighted_chnls[xx] = 0; + reserved_buff[xx] = 0; + } + + /* add up all of tx and rx data rate requirement */ + /* channel required higher data rate will get more buffer quota */ + for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++) + { + p_ccb = l2cb.ccb_pool + xx; + + if (!p_ccb->in_use) + continue; + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + weighted_chnls[p_ccb->ertm_info.user_tx_pool_id] += p_ccb->tx_data_rate; + weighted_chnls[p_ccb->ertm_info.user_rx_pool_id] += p_ccb->rx_data_rate; + + if (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) + { + /* reserve buffers only for wait_for_ack_q to maximize throughput */ + /* retrans_q will work based on buffer status */ + reserved_buff[HCI_ACL_POOL_ID] += p_ccb->peer_cfg.fcr.tx_win_sz; + } + + if (p_ccb->ertm_info.fcr_rx_pool_id == HCI_ACL_POOL_ID) + { + /* reserve buffers for srej_rcv_hold_q */ + reserved_buff[HCI_ACL_POOL_ID] += p_ccb->peer_cfg.fcr.tx_win_sz; + } + } + else + { + /* low data rate is 1, medium is 2, high is 3 and no traffic is 0 */ + weighted_chnls[HCI_ACL_POOL_ID] += p_ccb->tx_data_rate + p_ccb->rx_data_rate; + } + } + + + /* get unit quota per pool */ + for (xx = 0; xx < GKI_NUM_TOTAL_BUF_POOLS; xx++ ) + { + if ( weighted_chnls[xx] > 0 ) + { + if (GKI_poolcount(xx) > reserved_buff[xx]) + quota_per_weighted_chnls[xx] = ((GKI_poolcount(xx) - reserved_buff[xx])/weighted_chnls[xx]) + 1; + else + quota_per_weighted_chnls[xx] = 1; + + L2CAP_TRACE_DEBUG5 ("POOL ID:%d, GKI_poolcount = %d, reserved_buff = %d, weighted_chnls = %d, quota_per_weighted_chnls = %d", + xx, GKI_poolcount(xx), reserved_buff[xx], weighted_chnls[xx], quota_per_weighted_chnls[xx] ); + } + else + quota_per_weighted_chnls[xx] = 0; + } + + + /* assign buffer quota to each channel based on its data rate requirement */ + for (xx = 0; xx < MAX_L2CAP_CHANNELS; xx++) + { + p_ccb = l2cb.ccb_pool + xx; + + if (!p_ccb->in_use) + continue; + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + p_ccb->buff_quota = quota_per_weighted_chnls[p_ccb->ertm_info.user_tx_pool_id] * p_ccb->tx_data_rate; + + L2CAP_TRACE_EVENT6 ("CID:0x%04x FCR Mode:%u UserTxPool:%u Priority:%u TxDataRate:%u Quota:%u", + p_ccb->local_cid, p_ccb->peer_cfg.fcr.mode, p_ccb->ertm_info.user_tx_pool_id, + p_ccb->ccb_priority, p_ccb->tx_data_rate, p_ccb->buff_quota); + + } + else + { + p_ccb->buff_quota = quota_per_weighted_chnls[HCI_ACL_POOL_ID] * p_ccb->tx_data_rate; + + L2CAP_TRACE_EVENT4 ("CID:0x%04x Priority:%u TxDataRate:%u Quota:%u", + p_ccb->local_cid, + p_ccb->ccb_priority, p_ccb->tx_data_rate, p_ccb->buff_quota); + } + + /* quota may be change so check congestion */ + l2cu_check_channel_congestion (p_ccb); + } +} + +/******************************************************************************* +** +** Function l2c_link_processs_num_bufs +** +** Description This function is called when a "controller buffer size" +** event is first received from the controller. It updates +** the L2CAP values. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs) +{ + l2cb.num_lm_acl_bufs = l2cb.controller_xmit_window = num_lm_acl_bufs; + +} + +/******************************************************************************* +** +** Function l2c_link_pkts_rcvd +** +** Description This function is called from the HCI transport when it is time +** tto send a "Host ready for packets" command. This is only when +** host to controller flow control is used. If fills in the arrays +** of numbers of packets and handles. +** +** Returns count of number of entries filled in +** +*******************************************************************************/ +UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles) +{ + UINT8 num_found = 0; + +#if (L2CAP_HOST_FLOW_CTRL == TRUE) + + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->link_pkts_unacked)) + { + num_pkts[num_found] = p_lcb->link_pkts_unacked; + handles[num_found] = p_lcb->handle; + p_lcb->link_pkts_unacked = 0; + num_found++; + } + } + +#endif + + return (num_found); +} + +/******************************************************************************* +** +** Function l2c_link_role_changed +** +** Description This function is called whan a link's master/slave role change +** event is received. It simply updates the link control block. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status) +{ + tL2C_LCB *p_lcb; + int xx; + + /* Make sure not called from HCI Command Status (bd_addr and new_role are invalid) */ + if (bd_addr) + { + /* If here came form hci role change event */ + p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr); + if (p_lcb) + { + p_lcb->link_role = new_role; + + /* Reset high priority link if needed */ + if (hci_status == HCI_SUCCESS) + l2cu_set_acl_priority(bd_addr, p_lcb->acl_priority, TRUE); + } + } + + /* Check if any LCB was waiting for switch to be completed */ + for (xx = 0, p_lcb = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH)) + { + l2cu_create_conn_after_switch (p_lcb); + } + } +} + +/******************************************************************************* +** +** Function l2c_pin_code_request +** +** Description This function is called whan a pin-code request is received +** on a connection. If there are no channels active yet on the +** link, it extends the link first connection timer. Make sure +** that inactivity timer is not extended if PIN code happens +** to be after last ccb released. +** +** Returns void +** +*******************************************************************************/ +void l2c_pin_code_request (BD_ADDR bd_addr) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr); + + if ( (p_lcb) && (!p_lcb->ccb_queue.p_first_ccb) ) + { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT_EXT); + } +} + +#if ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE) +/******************************************************************************* +** +** Function l2c_link_check_power_mode +** +** Description This function is called to check power mode. +** +** Returns TRUE if link is going to be active from park +** FALSE if nothing to send or not in park mode +** +*******************************************************************************/ +BOOLEAN l2c_link_check_power_mode (tL2C_LCB *p_lcb) +{ + tBTM_PM_MODE mode; + tBTM_PM_PWR_MD pm; + tL2C_CCB *p_ccb; + BOOLEAN need_to_active = FALSE; + + /* + * We only switch park to active only if we have unsent packets + */ + if ( p_lcb->link_xmit_data_q.count == 0 ) + { + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + if (p_ccb->xmit_hold_q.count != 0) + { + need_to_active = TRUE; + break; + } + } + } + else + need_to_active = TRUE; + + /* if we have packets to send */ + if ( need_to_active ) + { + /* check power mode */ + if (BTM_ReadPowerMode(p_lcb->remote_bd_addr, &mode) == BTM_SUCCESS) + { + /* + if ( mode == BTM_PM_MD_PARK ) + { + L2CAP_TRACE_DEBUG1 ("LCB(0x%x) is in park mode", p_lcb->handle); +// Coverity: +// FALSE-POSITIVE error from Coverity test tool. Please do NOT remove following comment. +// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode + the other data members of tBTM_PM_PWR_MD are ignored + + memset((void*)&pm, 0, sizeof(pm)); + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, p_lcb->remote_bd_addr, &pm); + btu_start_timer (&p_lcb->timer_entry, + BTU_TTYPE_L2CAP_LINK, L2CAP_WAIT_UNPARK_TOUT); + return TRUE; + } + */ + if ( mode == BTM_PM_STS_PENDING ) + { + L2CAP_TRACE_DEBUG1 ("LCB(0x%x) is in PM pending state", p_lcb->handle); + + btu_start_timer (&p_lcb->timer_entry, + BTU_TTYPE_L2CAP_LINK, L2CAP_WAIT_UNPARK_TOUT); + return TRUE; + } + } + } + return FALSE; +} +#endif /* ((BTM_PWR_MGR_INCLUDED == TRUE) && L2CAP_WAKE_PARKED_LINK == TRUE) */ + +/******************************************************************************* +** +** Function l2c_link_check_send_pkts +** +** Description This function is called to check if it can send packets +** to the Host Controller. It may be passed the address of +** a packet to send. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf) +{ + int xx; + BOOLEAN single_write = FALSE; + + /* Save the channel ID for faster counting */ + if (p_buf) + { + if (p_ccb != NULL) + { + p_buf->event = p_ccb->local_cid; + single_write = TRUE; + } + else + p_buf->event = 0; + + p_buf->layer_specific = 0; + GKI_enqueue (&p_lcb->link_xmit_data_q, p_buf); + + if (p_lcb->link_xmit_quota == 0) + l2cb.check_round_robin = TRUE; + } + + /* If this is called from uncongested callback context break recursive calling. + ** This LCB will be served when receiving number of completed packet event. + */ + if (l2cb.is_cong_cback_context) + return; + + /* If we are in a scenario where there are not enough buffers for each link to + ** have at least 1, then do a round-robin for all the LCBs + */ + if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) + { + if (p_lcb == NULL) + p_lcb = l2cb.lcb_pool; + else if (!single_write) + p_lcb++; + + /* Loop through, starting at the next */ + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + /* If controller window is full, nothing to do */ + if ( (l2cb.controller_xmit_window == 0 +#if (BLE_INCLUDED == TRUE) + && !p_lcb->is_ble_link +#endif + ) +#if (BLE_INCLUDED == TRUE) + || (p_lcb->is_ble_link && l2cb.controller_le_xmit_window == 0 ) +#endif + || (l2cb.round_robin_unacked >= l2cb.round_robin_quota) ) + break; + + /* Check for wraparound */ + if (p_lcb == &l2cb.lcb_pool[MAX_L2CAP_LINKS]) + p_lcb = &l2cb.lcb_pool[0]; + + if ( (!p_lcb->in_use) + || (p_lcb->partial_segment_being_sent) + || (p_lcb->link_state != LST_CONNECTED) + || (p_lcb->link_xmit_quota != 0) + || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) + continue; + + /* See if we can send anything from the Link Queue */ + if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) != NULL) + { + l2c_link_send_to_lower (p_lcb, p_buf); + } + else if (single_write) + { + /* If only doing one write, break out */ + break; + } + /* If nothing on the link queue, check the channel queue */ + else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) + { + l2c_link_send_to_lower (p_lcb, p_buf); + } + } + + /* If we finished without using up our quota, no need for a safety check */ +#if (BLE_INCLUDED == TRUE) + if ( ((l2cb.controller_xmit_window > 0 && !p_lcb->is_ble_link) || + (l2cb.controller_le_xmit_window > 0 && p_lcb->is_ble_link)) + && (l2cb.round_robin_unacked < l2cb.round_robin_quota) ) +#else + if ( (l2cb.controller_xmit_window > 0) + && (l2cb.round_robin_unacked < l2cb.round_robin_quota) ) + +#endif + l2cb.check_round_robin = FALSE; + } + else /* if this is not round-robin service */ + { + /* If a partial segment is being sent, can't send anything else */ + if ( (p_lcb->partial_segment_being_sent) + || (p_lcb->link_state != LST_CONNECTED) + || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) + return; + + /* See if we can send anything from the link queue */ +#if (BLE_INCLUDED == TRUE) + while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) || + (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link)) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#else + while ( (l2cb.controller_xmit_window != 0) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#endif + { + if ((p_buf = (BT_HDR *)GKI_dequeue (&p_lcb->link_xmit_data_q)) == NULL) + break; + + if (!l2c_link_send_to_lower (p_lcb, p_buf)) + break; + } + + if (!single_write) + { + /* See if we can send anything for any channel */ +#if (BLE_INCLUDED == TRUE) + while ( ((l2cb.controller_xmit_window != 0 && !p_lcb->is_ble_link) || + (l2cb.controller_le_xmit_window != 0 && p_lcb->is_ble_link)) + && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#else + while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota)) +#endif + { + if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL) + break; + + if (!l2c_link_send_to_lower (p_lcb, p_buf)) + break; + } + } + + /* There is a special case where we have readjusted the link quotas and */ + /* this link may have sent anything but some other link sent packets so */ + /* so we may need a timer to kick off this link's transmissions. */ + if ( (p_lcb->link_xmit_data_q.count) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT); + } + +} + +/******************************************************************************* +** +** Function l2c_link_send_to_lower +** +** Description This function queues the buffer for HCI transmission +** +** Returns TRUE for success, FALSE for fail +** +*******************************************************************************/ +static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf) +{ + UINT16 num_segs; + UINT16 xmit_window, acl_data_size; + +#if (BLE_INCLUDED == TRUE) + if ((!p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_acl_pkt_size)) || + (p_lcb->is_ble_link && (p_buf->len <= btu_cb.hcit_ble_acl_pkt_size))) +#else + if (p_buf->len <= btu_cb.hcit_acl_pkt_size) +#endif + { + if (p_lcb->link_xmit_quota == 0) + l2cb.round_robin_unacked++; + + p_lcb->sent_not_acked++; + p_buf->layer_specific = 0; + +#if (BLE_INCLUDED == TRUE) + if (p_lcb->is_ble_link) + { + l2cb.controller_le_xmit_window--; + L2C_LINK_SEND_BLE_ACL_DATA (p_buf); + } + else +#endif + { + l2cb.controller_xmit_window--; + L2C_LINK_SEND_ACL_DATA (p_buf); + } + } + else + { +#if BLE_INCLUDED == TRUE + if (p_lcb->is_ble_link) + { + acl_data_size = btu_cb.hcit_ble_acl_data_size; + xmit_window = l2cb.controller_le_xmit_window; + + } + else +#endif + { + acl_data_size = btu_cb.hcit_acl_data_size; + xmit_window = l2cb.controller_xmit_window; + } + num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size; + + + /* If doing round-robin, then only 1 segment each time */ + if (p_lcb->link_xmit_quota == 0) + { + num_segs = 1; + p_lcb->partial_segment_being_sent = TRUE; + } + else + { + /* Multi-segment packet. Make sure it can fit */ + if (num_segs > xmit_window) + { + num_segs = xmit_window; + p_lcb->partial_segment_being_sent = TRUE; + } + + if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked)) + { + num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked); + p_lcb->partial_segment_being_sent = TRUE; + } + } + + p_buf->layer_specific = num_segs; +#if BLE_INCLUDED == TRUE + if (p_lcb->is_ble_link) + { + l2cb.controller_le_xmit_window -= num_segs; + + } + else +#endif + l2cb.controller_xmit_window -= num_segs; + + if (p_lcb->link_xmit_quota == 0) + l2cb.round_robin_unacked += num_segs; + + p_lcb->sent_not_acked += num_segs; +#if BLE_INCLUDED == TRUE + if (p_lcb->is_ble_link) + { + L2C_LINK_SEND_BLE_ACL_DATA(p_buf); + } + else +#endif + { + L2C_LINK_SEND_ACL_DATA (p_buf); + } + } + +#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE) +#if (BLE_INCLUDED == TRUE) + if (p_lcb->is_ble_link) + { + L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", + l2cb.controller_le_xmit_window, + p_lcb->handle, + p_lcb->link_xmit_quota, p_lcb->sent_not_acked, + l2cb.round_robin_quota, l2cb.round_robin_unacked); + } + else +#endif + { + L2CAP_TRACE_DEBUG6 ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d", + l2cb.controller_xmit_window, + p_lcb->handle, + p_lcb->link_xmit_quota, p_lcb->sent_not_acked, + l2cb.round_robin_quota, l2cb.round_robin_unacked); + } +#endif + + return TRUE; +} + +/******************************************************************************* +** +** Function l2c_link_process_num_completed_pkts +** +** Description This function is called when a "number-of-completed-packets" +** event is received from the controller. It updates all the +** LCB transmit counts. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_process_num_completed_pkts (UINT8 *p) +{ + UINT8 num_handles, xx; + UINT16 handle; + UINT16 num_sent; + tL2C_LCB *p_lcb; + + STREAM_TO_UINT8 (num_handles, p); + + for (xx = 0; xx < num_handles; xx++) + { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (num_sent, p); + + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* Callback for number of completed packet event */ + /* Originally designed for [3DSG] */ + if((p_lcb != NULL) && (p_lcb->p_nocp_cb)) + { + L2CAP_TRACE_DEBUG0 ("L2CAP - calling NoCP callback"); + (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr); + } + +#if (BLE_INCLUDED == TRUE) + if (p_lcb && p_lcb->is_ble_link) + l2cb.controller_le_xmit_window += num_sent; + else +#endif + { + + /* Maintain the total window to the controller */ + l2cb.controller_xmit_window += num_sent; + } + + if (p_lcb) + { + /* If doing round-robin, adjust communal counts */ + if (p_lcb->link_xmit_quota == 0) + { + /* Don't go negative */ + if (l2cb.round_robin_unacked > num_sent) + l2cb.round_robin_unacked -= num_sent; + else + l2cb.round_robin_unacked = 0; + } + + /* Don't go negative */ + if (p_lcb->sent_not_acked > num_sent) + p_lcb->sent_not_acked -= num_sent; + else + p_lcb->sent_not_acked = 0; + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + /* If we were doing round-robin for low priority links, check 'em */ + if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) + && (l2cb.check_round_robin) + && (l2cb.round_robin_unacked < l2cb.round_robin_quota) ) + { + l2c_link_check_send_pkts (NULL, NULL, NULL); + } + } + +#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE) + if (p_lcb) + { +#if (BLE_INCLUDED == TRUE) + if (p_lcb->is_ble_link) + { + L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d", + l2cb.controller_le_xmit_window, + p_lcb->handle, p_lcb->sent_not_acked, + l2cb.check_round_robin, l2cb.round_robin_unacked); + } + else +#endif + { + L2CAP_TRACE_DEBUG5 ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d", + l2cb.controller_xmit_window, + p_lcb->handle, p_lcb->sent_not_acked, + l2cb.check_round_robin, l2cb.round_robin_unacked); + + } + } + else + { +#if (BLE_INCLUDED == TRUE) + L2CAP_TRACE_DEBUG5 ("TotalWin=%d LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d", + l2cb.controller_xmit_window, + l2cb.controller_le_xmit_window, + handle, + l2cb.check_round_robin, l2cb.round_robin_unacked); +#else + L2CAP_TRACE_DEBUG4 ("TotalWin=%d Handle=0x%x RRCheck=%d RRUnack=%d", + l2cb.controller_xmit_window, + handle, + l2cb.check_round_robin, l2cb.round_robin_unacked); +#endif + } +#endif + } + +#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) + /* only full stack can enable sleep mode */ + btu_check_bt_sleep (); +#endif +} + +/******************************************************************************* +** +** Function l2cap_link_chk_pkt_start +** +** Description This function is called from the HCI transport when the first +** 4 bytes of an HCI ACL packet have been received. It checks if the +** packet is the next segment of a fragmented L2CAP message. If it +** is, and the length is OK, it returns the address of the +** starting L2CAP message segment buffer. +** +** Returns the address of the receive buffer HCIT should use +** (CR419: Modified to return NULL in case of error.) +** +** NOTE This assumes that the L2CAP MTU size is less than the size +** of an HCI ACL buffer, so the maximum L2CAP message will fit +** into one buffer. +** +*******************************************************************************/ +BT_HDR *l2cap_link_chk_pkt_start (BT_HDR *p_cur_buf) +{ + UINT8 *p; + UINT16 handle; + UINT16 hci_len; + UINT16 pkt_type; + tL2C_LCB *p_lcb; + BT_HDR * p_return_buf; /* CR419: To avoid returning from too many places */ + + + if (p_cur_buf) + { + p = (UINT8 *)(p_cur_buf + 1) + p_cur_buf->offset; + } + else + { + return (NULL); + } + + /* L2CAP expects all rcvd packets to have a layer-specific value of 0 */ + p_cur_buf->layer_specific = 0; + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (hci_len, p); + + pkt_type = HCID_GET_EVENT (handle); + handle = HCID_GET_HANDLE (handle); + + l2cb.p_cur_hcit_lcb = NULL; + + /* Find the link that is associated with this handle */ + p_lcb = l2cu_find_lcb_by_handle (handle); + + /* If no link for this handle, nothing to do. */ + if (!p_lcb) + return (p_cur_buf) ; + + if (pkt_type == L2CAP_PKT_START) /*** START PACKET ***/ + { + /* Start of packet. If we were in the middle of receiving */ + /* a packet, it is incomplete. Drop it. */ + if (p_lcb->p_hcit_rcv_acl) + { + L2CAP_TRACE_WARNING0 ("L2CAP - dropping incomplete pkt"); + GKI_freebuf (p_lcb->p_hcit_rcv_acl); + p_lcb->p_hcit_rcv_acl = NULL; + } + + /* Save the active buffer address in the LCB */ + if ((p_return_buf = p_cur_buf) != NULL) + { + p_lcb->p_hcit_rcv_acl = p_return_buf; + l2cb.p_cur_hcit_lcb = p_lcb; + } + } + else /*** CONTINUATION PACKET ***/ + { + /* Packet continuation. Check if we were expecting it */ + if (p_lcb->p_hcit_rcv_acl) + { + UINT16 total_len; + BT_HDR *p_base_buf = p_lcb->p_hcit_rcv_acl; + UINT8 *p_f = (UINT8 *)(p_base_buf + 1) + p_base_buf->offset + 2; + + STREAM_TO_UINT16 (total_len, p_f); + + /* We were expecting the CONTINUATION packet. If length fits, it can go in the */ + /* current buffer. */ + if ((total_len + hci_len) <= (L2CAP_MTU_SIZE + HCI_DATA_PREAMBLE_SIZE)) + { + /* GKI_freebuf (p_cur_buf); CR419:Do not free it yet */ + p_return_buf = p_lcb->p_hcit_rcv_acl; /* CR419: return base buffer */ + l2cb.p_cur_hcit_lcb = p_lcb; + + if ((p_cur_buf->len > HCI_DATA_PREAMBLE_SIZE)) + { + UINT8 * p = (UINT8 *)(p_cur_buf + 1) + + p_cur_buf->offset + + HCI_DATA_PREAMBLE_SIZE; + UINT8 * p1 = (UINT8 *)(p_return_buf + 1) + + p_return_buf->offset + + p_return_buf->len; + + /* Copy data from new buffer into base buffer then update the data */ + /* count in the base buffer accordingly. */ + memcpy (p1, p, p_cur_buf->len - HCI_DATA_PREAMBLE_SIZE); + p_return_buf->len += (p_cur_buf->len - HCI_DATA_PREAMBLE_SIZE); + } + + GKI_freebuf (p_cur_buf); + p_cur_buf = NULL; + + /* Update HCI header of first segment (base buffer) with new length */ + total_len += hci_len; + p_f = (UINT8 *)(p_base_buf + 1) + p_base_buf->offset + 2; + UINT16_TO_STREAM (p_f, total_len); + } + else + { + /* Packet too long. Drop the base packet */ + L2CAP_TRACE_WARNING3 ("L2CAP - dropping too long pkt BufLen: %d total_len: %d hci_len: %d", + p_lcb->p_hcit_rcv_acl->len, total_len, hci_len); + + GKI_freebuf (p_lcb->p_hcit_rcv_acl); + p_lcb->p_hcit_rcv_acl = NULL; + p_return_buf = NULL ; /* Can't hold onto it any more */ + } + } + else /*** NEITHER START OR CONTINUATION PACKET ***/ + { + p_return_buf = NULL ; + } + } + + if (p_return_buf == NULL) /* if error is indicated.. */ + { + if (p_cur_buf != NULL) /* ..drop input buffer */ + GKI_freebuf(p_cur_buf); /* (if present) */ + } + + return (p_return_buf); +} + +/******************************************************************************* +** +** Function l2cap_link_chk_pkt_end +** +** Description This function is called from the HCI transport when the last +** byte of an HCI ACL packet has been received. It checks if the +** L2CAP message is complete, i.e. no more continuation packets +** are expected. +** +** Returns TRUE if message complete, FALSE if continuation expected +** +*******************************************************************************/ +BOOLEAN l2cap_link_chk_pkt_end (void) +{ + UINT8 *p; + BT_HDR *p_buf; + UINT16 l2cap_len; + tL2C_LCB *p_lcb; + + /* If link or buffer pointer not set up, let main line handle it */ + if (((p_lcb = l2cb.p_cur_hcit_lcb) == NULL) || ((p_buf = p_lcb->p_hcit_rcv_acl) == NULL)) + return (TRUE); + + /* Point to the L2CAP length */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset + HCI_DATA_PREAMBLE_SIZE; + + STREAM_TO_UINT16 (l2cap_len, p); + + /* If the L2CAP length has not been reached, tell HCIT not to send this buffer to BTU */ + if (l2cap_len > (p_buf->len - (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD))) + { + return (FALSE); + } + else + { + p_lcb->p_hcit_rcv_acl = NULL; + return (TRUE); + } +} + + +/******************************************************************************* +** +** Function l2c_link_segments_xmitted +** +** Description This function is called from the HCI Interface when an ACL +** data packet segment is transmitted. +** +** Returns void +** +*******************************************************************************/ +void l2c_link_segments_xmitted (BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT16 handle; + tL2C_LCB *p_lcb; + + /* Extract the handle */ + STREAM_TO_UINT16 (handle, p); + handle = HCID_GET_HANDLE (handle); + + /* Find the LCB based on the handle */ + if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - rcvd segment complete, unknown handle: %d", handle); + GKI_freebuf (p_msg); + return; + } + + if (p_lcb->link_state == LST_CONNECTED) + { + /* Enqueue the buffer to the head of the transmit queue, and see */ + /* if we can transmit anything more. */ + GKI_enqueue_head (&p_lcb->link_xmit_data_q, p_msg); + + p_lcb->partial_segment_being_sent = FALSE; + + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + } + else + GKI_freebuf (p_msg); +} diff --git a/stack/l2cap/l2c_main.c b/stack/l2cap/l2c_main.c new file mode 100644 index 0000000..6c29cff --- /dev/null +++ b/stack/l2cap/l2c_main.c @@ -0,0 +1,991 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the main L2CAP entry points + * + ******************************************************************************/ + +#include "bt_target.h" +#include +#include +#include + +#include "gki.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "l2c_api.h" +#include "btu.h" +#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 process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len); + +/********************************************************************************/ +/* G L O B A L L 2 C A P D A T A */ +/********************************************************************************/ +#if L2C_DYNAMIC_MEMORY == FALSE +tL2C_CB l2cb; +#endif + +/* Temporary - until l2cap implements group management */ +#if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE) +extern void tcs_proc_bcst_msg( BD_ADDR addr, BT_HDR *p_msg ) ; + +/******************************************************************************* +** +** Function l2c_bcst_msg +** +** Description +** +** Returns void +** +*******************************************************************************/ +void l2c_bcst_msg( BT_HDR *p_buf, UINT16 psm ) +{ + UINT8 *p; + + /* Ensure we have enough space in the buffer for the L2CAP and HCI headers */ + if (p_buf->offset < L2CAP_BCST_MIN_OFFSET) + { + L2CAP_TRACE_ERROR1 ("L2CAP - cannot send buffer, offset: %d", p_buf->offset); + GKI_freebuf (p_buf); + return; + } + + /* Step back some bytes to add the headers */ + p_buf->offset -= (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD); + p_buf->len += L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD; + + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First, the HCI transport header */ + UINT16_TO_STREAM (p, 0x0050 | (L2CAP_PKT_START << 12) | (2 << 14)); + + /* The HCI transport will segment the buffers. */ + if (p_buf->len > btu_cb.hcit_acl_data_size) + { + UINT16_TO_STREAM (p, btu_cb.hcit_acl_data_size); + } + else + { + UINT16_TO_STREAM (p, p_buf->len); + } + + /* Now the L2CAP header */ + UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD); + UINT16_TO_STREAM (p, L2CAP_CONNECTIONLESS_CID); + UINT16_TO_STREAM (p, psm); + + p_buf->len += HCI_DATA_PREAMBLE_SIZE; + + if (p_buf->len <= btu_cb.hcit_acl_pkt_size) + { + HCI_ACL_DATA_TO_LOWER (p_buf); + } +} +#endif + +/******************************************************************************* +** +** Function l2c_rcv_acl_data +** +** Description This function is called from the HCI Interface when an ACL +** data packet is received. +** +** Returns void +** +*******************************************************************************/ +void l2c_rcv_acl_data (BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT16 handle, hci_len; + UINT8 pkt_type; + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb = NULL; + UINT16 l2cap_len, rcv_cid, psm; + + /* Extract the handle */ + STREAM_TO_UINT16 (handle, p); + pkt_type = HCID_GET_EVENT (handle); + handle = HCID_GET_HANDLE (handle); + + /* Since the HCI Transport is putting segmented packets back together, we */ + /* should never get a valid packet with the type set to "continuation" */ + if (pkt_type != L2CAP_PKT_CONTINUE) + { + /* Find the LCB based on the handle */ + if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) + { + UINT8 cmd_code; + + /* There is a slight possibility (specifically with USB) that we get an */ + /* L2CAP connection request before we get the HCI connection complete. */ + /* So for these types of messages, hold them for up to 2 seconds. */ + STREAM_TO_UINT16 (hci_len, p); + STREAM_TO_UINT16 (l2cap_len, p); + STREAM_TO_UINT16 (rcv_cid, p); + STREAM_TO_UINT8 (cmd_code, p); + + if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID) + && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ)) + { + L2CAP_TRACE_WARNING5 ("L2CAP - holding ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d", + handle, p_msg->layer_specific, rcv_cid, cmd_code, + l2cb.rcv_hold_q.count); + p_msg->layer_specific = 2; + GKI_enqueue (&l2cb.rcv_hold_q, p_msg); + + if (l2cb.rcv_hold_q.count == 1) + btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); + + return; + } + else + { + L2CAP_TRACE_ERROR5 ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d opcode:%d cur count:%d", + handle, p_msg->layer_specific, rcv_cid, cmd_code, l2cb.rcv_hold_q.count); + } + GKI_freebuf (p_msg); + return; + } + } + else + { + L2CAP_TRACE_WARNING1 ("L2CAP - expected pkt start or complete, got: %d", pkt_type); + GKI_freebuf (p_msg); + return; + } + + /* Extract the length and update the buffer header */ + STREAM_TO_UINT16 (hci_len, p); + p_msg->offset += 4; + +#if (L2CAP_HOST_FLOW_CTRL == TRUE) + /* Send ack if we hit the threshold */ + if (++p_lcb->link_pkts_unacked >= p_lcb->link_ack_thresh) + btu_hcif_send_host_rdy_for_data(); +#endif + + /* Extract the length and CID */ + STREAM_TO_UINT16 (l2cap_len, p); + STREAM_TO_UINT16 (rcv_cid, p); + + /* Find the CCB for this CID */ + if (rcv_cid >= L2CAP_BASE_APPL_CID) + { + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - unknown CID: 0x%04x", rcv_cid); + GKI_freebuf (p_msg); + return; + } + } + + if (hci_len >= L2CAP_PKT_OVERHEAD) /* Must receive at least the L2CAP length and CID.*/ + { + p_msg->len = hci_len - L2CAP_PKT_OVERHEAD; + p_msg->offset += L2CAP_PKT_OVERHEAD; + } + else + { + L2CAP_TRACE_WARNING0 ("L2CAP - got incorrect hci header" ); + GKI_freebuf (p_msg); + return; + } + + if (l2cap_len != p_msg->len) + { + L2CAP_TRACE_WARNING2 ("L2CAP - bad length in pkt. Exp: %d Act: %d", + l2cap_len, p_msg->len); + + GKI_freebuf (p_msg); + return; + } + + /* Send the data through the channel state machine */ + if (rcv_cid == L2CAP_SIGNALLING_CID) + { + process_l2cap_cmd (p_lcb, p, l2cap_len); + GKI_freebuf (p_msg); + } + else if (rcv_cid == L2CAP_CONNECTIONLESS_CID) + { + /* process_connectionless_data (p_lcb); */ + STREAM_TO_UINT16 (psm, p); + L2CAP_TRACE_DEBUG1( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ; +#if (TCS_BCST_SETUP_INCLUDED == TRUE && TCS_INCLUDED == TRUE) + if (psm == TCS_PSM_INTERCOM || psm == TCS_PSM_CORDLESS) + { + p_msg->offset += L2CAP_BCST_OVERHEAD; + p_msg->len -= L2CAP_BCST_OVERHEAD; + tcs_proc_bcst_msg( p_lcb->remote_bd_addr, p_msg ) ; + GKI_freebuf (p_msg); + } + else +#endif + +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if it is not broadcast, check UCD registration */ + if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) ) + { + /* nothing to do */ + } + else +#endif + GKI_freebuf (p_msg); + } +#if (BLE_INCLUDED == TRUE) + else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) + { + l2cble_process_sig_cmd (p_lcb, p, l2cap_len); + GKI_freebuf (p_msg); + } +#endif +#if (L2CAP_NUM_FIXED_CHNLS > 0) + else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) && + (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) ) + { + /* If no CCB for this channel, allocate one */ + if (l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) + { + p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL]; + + 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); + } + else + GKI_freebuf (p_msg); + } +#endif + + else + { + if (p_ccb == NULL) + GKI_freebuf (p_msg); + else + { + /* Basic mode packets go straight to the state machine */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg); + else + { + /* eRTM or streaming mode, so we need to validate states first */ + if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG)) + l2c_fcr_proc_pdu (p_ccb, p_msg); + else + GKI_freebuf (p_msg); + } + } + } +} + +/******************************************************************************* +** +** Function process_l2cap_cmd +** +** Description This function is called when a packet is received on the +** L2CAP signalling CID +** +** Returns void +** +*******************************************************************************/ +static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) +{ + UINT8 *p_pkt_end, *p_next_cmd, *p_cfg_end, *p_cfg_start; + UINT8 cmd_code, cfg_code, cfg_len, id; + tL2C_CONN_INFO con_info; + tL2CAP_CFG_INFO cfg_info; + UINT16 rej_reason, rej_mtu, lcid, rcid, info_type; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + BOOLEAN cfg_rej; + UINT16 cfg_rej_len, cmd_len; + UINT16 result; + tL2C_CONN_INFO ci; + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /* if l2cap command received in CID 1 on top of an LE link, ignore this command */ + if (p_lcb->is_ble_link) + return; +#endif + p_next_cmd = p; + p_pkt_end = p + pkt_len; + + memset (&cfg_info, 0, sizeof(cfg_info)); + + /* An L2CAP packet may contain multiple commands */ + while (TRUE) + { + /* Smallest command is 4 bytes */ + if ((p = p_next_cmd) > (p_pkt_end - 4)) + break; + + STREAM_TO_UINT8 (cmd_code, p); + STREAM_TO_UINT8 (id, p); + STREAM_TO_UINT16 (cmd_len, p); + + /* Check command length does not exceed packet length */ + if ((p_next_cmd = p + cmd_len) > p_pkt_end) + { + L2CAP_TRACE_WARNING3 ("Command len bad pkt_len: %d cmd_len: %d code: %d", + pkt_len, cmd_len, cmd_code); + break; + } + + switch (cmd_code) + { + case L2CAP_CMD_REJECT: + STREAM_TO_UINT16 (rej_reason, p); + if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) + { + STREAM_TO_UINT16 (rej_mtu, p); + /* What to do with the MTU reject ? We have negotiated an MTU. For now */ + /* we will ignore it and let a higher protocol timeout take care of it */ + + L2CAP_TRACE_WARNING2 ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu); + } + if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) + { + STREAM_TO_UINT16 (rcid, p); + STREAM_TO_UINT16 (lcid, p); + + L2CAP_TRACE_WARNING2 ("L2CAP - rej with CID invalid, LCID: 0x%04x RCID: 0x%04x", lcid, rcid); + + /* Remote CID invalid. Treat as a disconnect */ + if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + && (p_ccb->remote_cid == rcid)) + { + /* Fake link disconnect - no reply is generated */ + l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); + } + } + + /* SonyEricsson Info request Bug workaround (Continue connection) */ + else if (rej_reason == L2CAP_CMD_REJ_NOT_UNDERSTOOD && p_lcb->w4_info_rsp) + { + btu_stop_timer (&p_lcb->info_timer_entry); + + p_lcb->w4_info_rsp = FALSE; + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + } + break; + + case L2CAP_CMD_CONN_REQ: + STREAM_TO_UINT16 (con_info.psm, p); + STREAM_TO_UINT16 (rcid, p); + if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); + break; + } + else + { + if (!p_rcb->api.pL2CA_ConnectInd_Cb) + { + L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for outgoing-only connection PSM: %d", con_info.psm); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); + break; + } + } + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) + { + L2CAP_TRACE_ERROR0 ("L2CAP - unable to allocate CCB"); + l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_RESOURCES); + break; + } + p_ccb->remote_id = id; + p_ccb->p_rcb = p_rcb; + p_ccb->remote_cid = rcid; + + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info); + break; + + case L2CAP_CMD_CONN_RSP: + STREAM_TO_UINT16 (con_info.remote_cid, p); + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (con_info.l2cap_result, p); + STREAM_TO_UINT16 (con_info.l2cap_status, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) + { + L2CAP_TRACE_WARNING2 ("L2CAP - no CCB for conn rsp, LCID: %d RCID: %d", + lcid, con_info.remote_cid); + break; + } + if (p_ccb->local_id != id) + { + L2CAP_TRACE_WARNING2 ("L2CAP - con rsp - bad ID. Exp: %d Got: %d", + p_ccb->local_id, id); + break; + } + + if (con_info.l2cap_result == L2CAP_CONN_OK) + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP, &con_info); + else if (con_info.l2cap_result == L2CAP_CONN_PENDING) + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_PND, &con_info); + else + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_NEG, &con_info); + + break; + + case L2CAP_CMD_CONFIG_REQ: + p_cfg_end = p + cmd_len; + cfg_rej = FALSE; + cfg_rej_len = 0; + + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (cfg_info.flags, p); + + p_cfg_start = p; + + cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = + cfg_info.fcr_present = cfg_info.fcs_present = FALSE; + + while (p < p_cfg_end) + { + STREAM_TO_UINT8 (cfg_code, p); + STREAM_TO_UINT8 (cfg_len, p); + + switch (cfg_code & 0x7F) + { + case L2CAP_CFG_TYPE_MTU: + cfg_info.mtu_present = TRUE; + STREAM_TO_UINT16 (cfg_info.mtu, p); + break; + + case L2CAP_CFG_TYPE_FLUSH_TOUT: + cfg_info.flush_to_present = TRUE; + STREAM_TO_UINT16 (cfg_info.flush_to, p); + break; + + case L2CAP_CFG_TYPE_QOS: + cfg_info.qos_present = TRUE; + STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); + STREAM_TO_UINT8 (cfg_info.qos.service_type, p); + STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); + STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); + STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); + STREAM_TO_UINT32 (cfg_info.qos.latency, p); + STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); + break; + + case L2CAP_CFG_TYPE_FCR: + cfg_info.fcr_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcr.mode, p); + STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); + STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); + STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mps, p); + break; + + case L2CAP_CFG_TYPE_FCS: + cfg_info.fcs_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcs, p); + break; + + case L2CAP_CFG_TYPE_EXT_FLOW: + cfg_info.ext_flow_spec_present = TRUE; + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); + STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); + break; + + default: + /* sanity check option length */ + if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len) + { + p += cfg_len; + if ((cfg_code & 0x80) == 0) + { + cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + cfg_rej = TRUE; + } + } + /* bad length; force loop exit */ + else + { + p = p_cfg_end; + cfg_rej = TRUE; + } + break; + } + } + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + { + p_ccb->remote_id = id; + if (cfg_rej) + { + l2cu_send_peer_config_rej (p_ccb, p_cfg_start, (UINT16) (cmd_len - L2CAP_CONFIG_REQ_LEN), cfg_rej_len); + } + else + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info); + } + } + else + { + /* updated spec says send command reject on invalid cid */ + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0); + } + break; + + case L2CAP_CMD_CONFIG_RSP: + p_cfg_end = p + cmd_len; + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (cfg_info.flags, p); + STREAM_TO_UINT16 (cfg_info.result, p); + + cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = + cfg_info.fcr_present = cfg_info.fcs_present = FALSE; + + while (p < p_cfg_end) + { + STREAM_TO_UINT8 (cfg_code, p); + STREAM_TO_UINT8 (cfg_len, p); + + switch (cfg_code & 0x7F) + { + case L2CAP_CFG_TYPE_MTU: + cfg_info.mtu_present = TRUE; + STREAM_TO_UINT16 (cfg_info.mtu, p); + break; + + case L2CAP_CFG_TYPE_FLUSH_TOUT: + cfg_info.flush_to_present = TRUE; + STREAM_TO_UINT16 (cfg_info.flush_to, p); + break; + + case L2CAP_CFG_TYPE_QOS: + cfg_info.qos_present = TRUE; + STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); + STREAM_TO_UINT8 (cfg_info.qos.service_type, p); + STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); + STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); + STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); + STREAM_TO_UINT32 (cfg_info.qos.latency, p); + STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); + break; + + case L2CAP_CFG_TYPE_FCR: + cfg_info.fcr_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcr.mode, p); + STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); + STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); + STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); + STREAM_TO_UINT16 (cfg_info.fcr.mps, p); + break; + + case L2CAP_CFG_TYPE_FCS: + cfg_info.fcs_present = TRUE; + STREAM_TO_UINT8 (cfg_info.fcs, p); + break; + + case L2CAP_CFG_TYPE_EXT_FLOW: + cfg_info.ext_flow_spec_present = TRUE; + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); + STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); + STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); + STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); + break; + } + } + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + { + if (p_ccb->local_id != id) + { + L2CAP_TRACE_WARNING2 ("L2CAP - cfg rsp - bad ID. Exp: %d Got: %d", + p_ccb->local_id, id); + break; + } + if ( (cfg_info.result == L2CAP_CFG_OK) || (cfg_info.result == L2CAP_CFG_PENDING) ) + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info); + else + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info); + } + else + { + L2CAP_TRACE_WARNING1 ("L2CAP - rcvd cfg rsp for unknown CID: 0x%04x", lcid); + } + break; + + + case L2CAP_CMD_DISC_REQ: + STREAM_TO_UINT16 (lcid, p); + STREAM_TO_UINT16 (rcid, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + { + if (p_ccb->remote_cid == rcid) + { + p_ccb->remote_id = id; + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info); + } + } + else + l2cu_send_peer_disc_rsp (p_lcb, id, lcid, rcid); + + break; + + case L2CAP_CMD_DISC_RSP: + STREAM_TO_UINT16 (rcid, p); + STREAM_TO_UINT16 (lcid, p); + + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) + { + if ((p_ccb->remote_cid == rcid) && (p_ccb->local_id == id)) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &con_info); + } + } + break; + + case L2CAP_CMD_ECHO_REQ: + +#if (L2CAP_ENHANCED_FEATURES != 0) + if (!l2cu_check_feature_req (p_lcb, id, p, cmd_len)) + { + if (cmd_len < (btu_cb.hcit_acl_pkt_size - L2CAP_PKT_OVERHEAD + - L2CAP_CMD_OVERHEAD + - L2CAP_ECHO_RSP_LEN + - HCI_DATA_PREAMBLE_SIZE)) + { + l2cu_send_peer_echo_rsp (p_lcb, id, p, cmd_len); + } + else + { + l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0); + } + } +#else + l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0); +#endif + break; + + case L2CAP_CMD_ECHO_RSP: +#if (L2CAP_ENHANCED_FEATURES != 0) + l2cu_check_feature_rsp (p_lcb, id, p, cmd_len); +#endif + if (p_lcb->p_echo_rsp_cb) + { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_OK); + } + break; + + case L2CAP_CMD_INFO_REQ: + STREAM_TO_UINT16 (info_type, p); + l2cu_send_peer_info_rsp (p_lcb, id, info_type); + break; + + case L2CAP_CMD_INFO_RSP: + /* Stop the link connect timer if sent before L2CAP connection is up */ + if (p_lcb->w4_info_rsp) + { + btu_stop_timer (&p_lcb->info_timer_entry); + p_lcb->w4_info_rsp = FALSE; + } + + STREAM_TO_UINT16 (info_type, p); + STREAM_TO_UINT16 (result, p); + + p_lcb->info_rx_bits |= (1 << info_type); + + if ( (info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (result == L2CAP_INFO_RESP_RESULT_SUCCESS) ) + { + STREAM_TO_UINT32( p_lcb->peer_ext_fea, p ); + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (p_lcb->peer_ext_fea & L2CAP_EXTFEA_FIXED_CHNLS) + { + l2cu_send_peer_info_req (p_lcb, L2CAP_FIXED_CHANNELS_INFO_TYPE); + break; + } + else + { + l2cu_process_fixed_chnl_resp (p_lcb); + } +#endif + } + + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) + { + if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) + { + memcpy (p_lcb->peer_chnl_mask, p, L2CAP_FIXED_CHNL_ARRAY_SIZE); + } + + l2cu_process_fixed_chnl_resp (p_lcb); + } +#endif +#if (L2CAP_UCD_INCLUDED == TRUE) + else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) + { + if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) + { + STREAM_TO_UINT16 (p_lcb->ucd_mtu, p); + } + } +#endif + + ci.status = HCI_SUCCESS; + memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); + } + break; + + default: + L2CAP_TRACE_WARNING1 ("L2CAP - bad cmd code: %d", cmd_code); + l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); + return; + } + } +} + +/******************************************************************************* +** +** Function l2c_process_held_packets +** +** Description This function processes any L2CAP packets that arrived before +** the HCI connection complete arrived. It is a work around for +** badly behaved controllers. +** +** Returns void +** +*******************************************************************************/ +void l2c_process_held_packets (BOOLEAN timed_out) +{ + BT_HDR *p_buf, *p_buf1; + BUFFER_Q *p_rcv_hold_q = &l2cb.rcv_hold_q; + + if (!p_rcv_hold_q->count) + return; + + if (!timed_out) + { + btu_stop_timer(&l2cb.rcv_hold_tle); + L2CAP_TRACE_WARNING0("L2CAP HOLD CONTINUE"); + } + else + { + L2CAP_TRACE_WARNING0("L2CAP HOLD TIMEOUT"); + } + + /* Update the timeouts in the hold queue */ + for (p_buf = (BT_HDR *)GKI_getfirst (p_rcv_hold_q); p_buf; p_buf = p_buf1) + { + p_buf1 = (BT_HDR *)GKI_getnext (p_buf); + if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0)) + { + GKI_remove_from_queue (p_rcv_hold_q, p_buf); + p_buf->layer_specific = 0xFFFF; + l2c_rcv_acl_data (p_buf); + } + } + + /* If anyone still in the queue, restart the timeout */ + if (p_rcv_hold_q->count) + btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); +} + + +/******************************************************************************* +** +** Function l2c_init +** +** Description This function is called once at startup to initialize +** all the L2CAP structures +** +** Returns void +** +*******************************************************************************/ +void l2c_init (void) +{ + INT16 xx; + + memset (&l2cb, 0, sizeof (tL2C_CB)); + /* the psm is increased by 2 before being used */ + l2cb.dyn_psm = 0xFFF; + + /* Put all the channel control blocks on the free queue */ + for (xx = 0; xx < MAX_L2CAP_CHANNELS - 1; xx++) + { + l2cb.ccb_pool[xx].p_next_ccb = &l2cb.ccb_pool[xx + 1]; + } + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + /* it will be set to L2CAP_PKT_START_NON_FLUSHABLE if controller supports */ + l2cb.non_flushable_pbf = L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT; +#endif + + + l2cb.p_free_ccb_first = &l2cb.ccb_pool[0]; + l2cb.p_free_ccb_last = &l2cb.ccb_pool[MAX_L2CAP_CHANNELS - 1]; + +#ifdef L2CAP_DESIRED_LINK_ROLE + l2cb.desire_role = L2CAP_DESIRED_LINK_ROLE; +#else + l2cb.desire_role = HCI_ROLE_SLAVE; +#endif + + /* Set the default idle timeout */ + l2cb.idle_timeout = L2CAP_LINK_INACTIVITY_TOUT; + +#if defined(L2CAP_INITIAL_TRACE_LEVEL) + l2cb.l2cap_trace_level = L2CAP_INITIAL_TRACE_LEVEL; +#else + l2cb.l2cap_trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + +#if L2CAP_CONFORMANCE_TESTING == TRUE + /* Conformance testing needs a dynamic response */ + l2cb.test_info_resp = L2CAP_EXTFEA_SUPPORTED_MASK; +#endif + + /* Number of ACL buffers to use for high priority channel */ +#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE)) + l2cb.high_pri_min_xmit_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA; +#endif + +} + +/******************************************************************************* +** +** Function l2c_process_timeout +** +** Description This function is called when an L2CAP-related timeout occurs +** +** Returns void +** +*******************************************************************************/ +void l2c_process_timeout (TIMER_LIST_ENT *p_tle) +{ + /* What type of timeout ? */ + switch (p_tle->event) + { + case BTU_TTYPE_L2CAP_LINK: + l2c_link_timeout ((tL2C_LCB *)p_tle->param); + break; + + case BTU_TTYPE_L2CAP_CHNL: + l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL); + break; + + case BTU_TTYPE_L2CAP_FCR_ACK: + l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_ACK_TIMEOUT, NULL); + break; + + case BTU_TTYPE_L2CAP_HOLD: + /* Update the timeouts in the hold queue */ + l2c_process_held_packets(TRUE); + break; + + case BTU_TTYPE_L2CAP_INFO: + l2c_info_timeout((tL2C_LCB *)p_tle->param); + break; + + } +} + +/******************************************************************************* +** +** Function l2c_data_write +** +** Description API functions call this function to write data. +** +** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE +** L2CAP_DW_CONGESTED, if data accepted and the channel is congested +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags) +{ + tL2C_CCB *p_ccb; + + /* Find the channel control block. We don't know the link it is on. */ + if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid); + GKI_freebuf (p_data); + return (L2CAP_DW_FAILED); + } + +#ifndef TESTER /* Tester may send any amount of data. otherwise sending message + bigger than mtu size of peer is a violation of protocol */ + if (p_data->len > p_ccb->peer_cfg.mtu) + { + L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid); + GKI_freebuf (p_data); + return (L2CAP_DW_FAILED); + } +#endif + + /* channel based, packet based flushable or non-flushable */ + p_data->layer_specific = flags; + + /* If already congested, do not accept any more packets */ + if (p_ccb->cong_sent) + { + L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u", + p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota); + + GKI_freebuf (p_data); + return (L2CAP_DW_FAILED); + } + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data); + + if (p_ccb->cong_sent) + return (L2CAP_DW_CONGESTED); + + return (L2CAP_DW_SUCCESS); +} + diff --git a/stack/l2cap/l2c_ucd.c b/stack/l2cap/l2c_ucd.c new file mode 100644 index 0000000..4dfc804 --- /dev/null +++ b/stack/l2cap/l2c_ucd.c @@ -0,0 +1,1170 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the L2CAP UCD code + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" + +#if (L2CAP_UCD_INCLUDED == TRUE) +static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda ); + +/******************************************************************************* +** +** Function l2c_ucd_discover_cback +** +** Description UCD Discover callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_discover_cback (BD_ADDR rem_bda, UINT8 info_type, UINT32 data) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_discover_cback"); + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (p_rcb->in_use) + { + /* if this application is waiting UCD reception info */ + if (( info_type == L2CAP_UCD_INFO_TYPE_RECEPTION ) + && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION )) + { + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data); + p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_RECEPTION); + } + + /* if this application is waiting UCD MTU info */ + if (( info_type == L2CAP_UCD_INFO_TYPE_MTU ) + && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU )) + { + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data); + p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_MTU); + } + } + } +} + +/******************************************************************************* +** +** Function l2c_ucd_data_ind_cback +** +** Description UCD Data callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_data_ind_cback (BD_ADDR rem_bda, BT_HDR *p_buf) +{ + UINT8 *p; + UINT16 psm; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_data_ind_cback"); + + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT16(psm, p) + + p_buf->offset += L2CAP_UCD_OVERHEAD; + p_buf->len -= L2CAP_UCD_OVERHEAD; + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + { + L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for l2c_ucd_data_ind_cback, PSM: 0x%04x", psm); + GKI_freebuf (p_buf); + } + else + { + p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(rem_bda, p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_congestion_status_cback +** +** Description UCD Congestion Status callback +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_congestion_status_cback (BD_ADDR rem_bda, BOOLEAN is_congested) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + L2CAP_TRACE_DEBUG0 ("L2CAP - l2c_ucd_congestion_status_cback"); + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (( p_rcb->in_use ) + &&( p_rcb->ucd.state != L2C_UCD_STATE_UNUSED )) + { + if ( p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) + { + L2CAP_TRACE_DEBUG4 ("L2CAP - Calling UCDCongestionStatus_Cb (%d), PSM=0x%04x, BDA: %08x%04x,", + is_congested, p_rcb->psm, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5]); + + p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ( rem_bda, is_congested ); + } + } + } +} + +/******************************************************************************* +** +** Function l2c_ucd_disconnect_ind_cback +** +** Description UCD disconnect callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_disconnect_ind_cback (UINT16 cid, BOOLEAN result) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function l2c_ucd_config_ind_cback +** +** Description UCD config callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_config_ind_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function l2c_ucd_config_cfm_cback +** +** Description UCD config callback (This prevent to access null pointer) +** +** Returns void +** +*******************************************************************************/ +static void l2c_ucd_config_cfm_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg) +{ + /* do nothing */ +} + +/******************************************************************************* +** +** Function L2CA_UcdRegister +** +** Description Register PSM on UCD. +** +** Parameters: tL2CAP_UCD_CB_INFO +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info ) +{ + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API1 ("L2CA_UcdRegister() PSM: 0x%04x", psm); + + if ((!p_cb_info->pL2CA_UCD_Discover_Cb) + || (!p_cb_info->pL2CA_UCD_Data_Cb)) + { + L2CAP_TRACE_ERROR1 ("L2CAP - no callback registering PSM(0x%04x) on UCD", psm); + return (FALSE); + } + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + { + L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for L2CA_UcdRegister, PSM: 0x%04x", psm); + return (FALSE); + } + + p_rcb->ucd.state = L2C_UCD_STATE_W4_DATA; + p_rcb->ucd.cb_info = *p_cb_info; + + /* check if master rcb is created for UCD */ + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) + { + if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID)) == NULL) + { + L2CAP_TRACE_ERROR0 ("L2CAP - no RCB available for L2CA_UcdRegister"); + return (FALSE); + } + else + { + /* these callback functions will forward data to each UCD application */ + p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb = l2c_ucd_discover_cback; + p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb = l2c_ucd_data_ind_cback; + p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb = l2c_ucd_congestion_status_cback; + + memset (&p_rcb->api, 0, sizeof(tL2CAP_APPL_INFO)); + p_rcb->api.pL2CA_DisconnectInd_Cb = l2c_ucd_disconnect_ind_cback; + + /* This will make L2CAP check UCD congestion callback */ + p_rcb->api.pL2CA_CongestionStatus_Cb = NULL; + + /* do nothing but prevent crash */ + p_rcb->api.pL2CA_ConfigInd_Cb = l2c_ucd_config_ind_cback; + p_rcb->api.pL2CA_ConfigCfm_Cb = l2c_ucd_config_cfm_cback; + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDeregister +** +** Description Deregister PSM on UCD. +** +** Parameters: PSM +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdDeregister ( UINT16 psm ) +{ + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + UINT16 xx; + + L2CAP_TRACE_API1 ("L2CA_UcdDeregister() PSM: 0x%04x", psm); + + if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + { + L2CAP_TRACE_ERROR1 ("L2CAP - no RCB for L2CA_UcdDeregister, PSM: 0x%04x", psm); + return (FALSE); + } + + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; + + /* check this was the last UCD registration */ + p_rcb = &l2cb.rcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if ((p_rcb->in_use) && (p_rcb->ucd.state != L2C_UCD_STATE_UNUSED)) + return (TRUE); + } + + /* delete master rcb for UCD */ + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL) + { + l2cu_release_rcb (p_rcb); + } + + /* delete CCB for UCD */ + p_ccb = l2cb.ccb_pool; + for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ ) + { + if (( p_ccb->in_use ) + &&( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )) + { + l2cu_release_ccb (p_ccb); + } + p_ccb++; + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDiscover +** +** Description Discover UCD of remote device. +** +** Parameters: PSM +** BD_ADDR of remote device +** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION +** L2CAP_UCD_INFO_TYPE_MTU +** +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_API4 ("L2CA_UcdDiscover() PSM: 0x%04x BDA: %08x%04x, InfoType=0x%02x", psm, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5], info_type); + + /* Fail if the PSM is not registered */ + if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_UcdDiscover, PSM: 0x%04x", psm); + return (FALSE); + } + + /* First, see if we already have a link to the remote */ + /* then find the channel control block for UCD. */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) + { + if ( l2c_ucd_connect (rem_bda) == FALSE ) + { + return (FALSE); + } + } + + /* set waiting flags in rcb */ + + if ( info_type & L2CAP_UCD_INFO_TYPE_RECEPTION ) + p_rcb->ucd.state |= L2C_UCD_STATE_W4_RECEPTION; + + if ( info_type & L2CAP_UCD_INFO_TYPE_MTU ) + p_rcb->ucd.state |= L2C_UCD_STATE_W4_MTU; + + /* if link is already established */ + if ((p_lcb)&&(p_lcb->link_state == LST_CONNECTED)) + { + if (!p_ccb) + { + p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID); + } + l2c_ucd_check_pending_info_req(p_ccb); + } + return (TRUE); +} + +/******************************************************************************* +** +** Function L2CA_UcdDataWrite +** +** Description Send UCD to remote device +** +** Parameters: PSM +** BD Address of remote +** Pointer to buffer of type BT_HDR +** flags : L2CAP_FLUSHABLE_CH_BASED +** L2CAP_FLUSHABLE_PKT +** L2CAP_NON_FLUSHABLE_PKT +** +** Return value L2CAP_DW_SUCCESS, if data accepted +** L2CAP_DW_FAILED, if error +** +*******************************************************************************/ +UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + UINT8 *p; + + L2CAP_TRACE_API3 ("L2CA_UcdDataWrite() PSM: 0x%04x BDA: %08x%04x", psm, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5]); + + /* Fail if the PSM is not registered */ + if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) + ||( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) + { + L2CAP_TRACE_WARNING1 ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm); + GKI_freebuf (p_buf); + return (L2CAP_DW_FAILED); + } + + /* First, see if we already have a link to the remote */ + /* then find the channel control block for UCD */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) + { + if ( l2c_ucd_connect (rem_bda) == FALSE ) + { + GKI_freebuf (p_buf); + return (L2CAP_DW_FAILED); + } + + /* If we still don't have lcb and ccb after connect attempt, then can't proceed */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) + { + GKI_freebuf (p_buf); + return (L2CAP_DW_FAILED); + } + } + + /* write PSM */ + p_buf->offset -= L2CAP_UCD_OVERHEAD; + p_buf->len += L2CAP_UCD_OVERHEAD; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + UINT16_TO_STREAM (p, psm); + + /* UCD MTU check */ + if ((p_lcb->ucd_mtu) && (p_buf->len > p_lcb->ucd_mtu)) + { + L2CAP_TRACE_WARNING1 ("L2CAP - Handle: 0x%04x UCD bigger than peer's UCD mtu size cannot be sent", p_lcb->handle); + GKI_freebuf (p_buf); + return (L2CAP_DW_FAILED); + } + + /* If already congested, do not accept any more packets */ + if (p_ccb->cong_sent) + { + L2CAP_TRACE_ERROR3 ("L2CAP - Handle: 0x%04x UCD cannot be sent, already congested count: %u buff_quota: %u", + p_lcb->handle, + (p_ccb->xmit_hold_q.count + p_lcb->ucd_out_sec_pending_q.count), + p_ccb->buff_quota); + + GKI_freebuf (p_buf); + return (L2CAP_DW_FAILED); + } + + /* channel based, packet based flushable or non-flushable */ + p_buf->layer_specific = flags; + + l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_buf); + + if (p_ccb->cong_sent) + return (L2CAP_DW_CONGESTED); + else + return (L2CAP_DW_SUCCESS); +} + +/******************************************************************************* +** +** Function L2CA_UcdSetIdleTimeout +** +** Description Set UCD Idle timeout. +** +** Parameters: BD Addr +** Timeout in second +** +** Return value: TRUE if successs +** +*******************************************************************************/ +BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API3 ("L2CA_UcdSetIdleTimeout() Timeout: 0x%04x BDA: %08x%04x", timeout, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5]); + + /* First, see if we already have a link to the remote */ + /* then find the channel control block. */ + if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + ||((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no UCD channel"); + return (FALSE); + } + else + { + p_ccb->fixed_chnl_idle_tout = timeout; + return (TRUE); + } +} + +/******************************************************************************* +** +** Function L2CA_UCDSetTxPriority +** +** Description Sets the transmission priority for a connectionless channel. +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ +BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + + L2CAP_TRACE_API3 ("L2CA_UCDSetTxPriority() priority: 0x%02x BDA: %08x%04x", priority, + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5]); + + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_UCDSetTxPriority"); + return (FALSE); + } + + /* Find the channel control block */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for L2CA_UCDSetTxPriority"); + return (FALSE); + } + + /* it will update the order of CCB in LCB by priority and update round robin service variables */ + l2cu_change_pri_ccb (p_ccb, priority); + + return (TRUE); +} + +/******************************************************************************* +** +** Function l2c_ucd_connect +** +** Description Connect UCD to remote device. +** +** Parameters: BD_ADDR of remote device +** +** Return value: TRUE if successs +** +*******************************************************************************/ +static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda ) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + L2CAP_TRACE_DEBUG2 ("l2c_ucd_connect() BDA: %08x%04x", + (rem_bda[0]<<24)+(rem_bda[1]<<16)+(rem_bda[2]<<8)+rem_bda[3], + (rem_bda[4]<<8)+rem_bda[5]); + + /* Fail if we have not established communications with the controller */ + if (!BTM_IsDeviceUp()) + { + L2CAP_TRACE_WARNING0 ("l2c_ucd_connect - BTU not ready"); + return (FALSE); + } + + /* First, see if we already have a link to the remote */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda)) == NULL) + { + /* No link. Get an LCB and start link establishment */ + if ( ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE)) == NULL) + || (l2cu_create_conn(p_lcb) == FALSE) ) + { + L2CAP_TRACE_WARNING0 ("L2CAP - conn not started l2c_ucd_connect"); + return (FALSE); + } + } + else if ( p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) + { + if (!(p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) + { + L2CAP_TRACE_WARNING0 ("L2CAP - UCD is not supported by peer, l2c_ucd_connect"); + return (FALSE); + } + } + + /* Find the channel control block. */ + if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) + { + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for l2c_ucd_connect"); + return (FALSE); + } + else + { + /* Set CID for the connection */ + p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID; + p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID; + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT; + + /* Set the default channel priority value to use */ + l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY); + + if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no UCD registered, l2c_ucd_connect"); + return (FALSE); + } + /* Save UCD registration info */ + p_ccb->p_rcb = p_rcb; + + /* There is no configuration, so if the link is up, the channel is up */ + if (p_lcb->link_state == LST_CONNECTED) + { + p_ccb->chnl_state = CST_OPEN; + } + } + } + + return (TRUE); +} + +/******************************************************************************* +** +** Function l2c_ucd_delete_sec_pending_q +** +** Description discard all of UCD packets in security pending queue +** +** Returns None +** +*******************************************************************************/ +void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb) +{ + /* clean up any security pending UCD */ + while (p_lcb->ucd_out_sec_pending_q.p_first) + GKI_freebuf (GKI_dequeue (&p_lcb->ucd_out_sec_pending_q)); + + while (p_lcb->ucd_in_sec_pending_q.p_first) + GKI_freebuf (GKI_dequeue (&p_lcb->ucd_in_sec_pending_q)); +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_info_req +** +** Description check if any application is waiting for UCD information +** +** Return TRUE if any pending UCD info request +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + BOOLEAN pending = FALSE; + + if (p_ccb == NULL) + { + L2CAP_TRACE_ERROR0 ("L2CAP - NULL p_ccb in l2c_ucd_check_pending_info_req"); + return (FALSE); + } + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (p_rcb->in_use) + { + /* if application is waiting UCD reception info */ + if (p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION) + { + /* if this information is available */ + if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) + { + if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) + { + L2CAP_TRACE_WARNING0 ("L2CAP - UCD is not supported by peer, l2c_ucd_check_pending_info_req"); + + l2c_ucd_delete_sec_pending_q(p_ccb->p_lcb); + l2cu_release_ccb (p_ccb); + } + + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr, + L2CAP_UCD_INFO_TYPE_RECEPTION, + p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION); + } + else + { + pending = TRUE; + if (p_ccb->p_lcb->w4_info_rsp == FALSE) + { + l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE); + } + } + } + + /* if application is waiting for UCD MTU */ + if (p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU) + { + /* if this information is available */ + if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_CONNLESS_MTU_INFO_TYPE)) + { + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr, + L2CAP_UCD_INFO_TYPE_MTU, + p_ccb->p_lcb->ucd_mtu); + } + else + { + pending = TRUE; + if (p_ccb->p_lcb->w4_info_rsp == FALSE) + { + l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_CONNLESS_MTU_INFO_TYPE); + } + } + } + } + } + return (pending); +} + +/******************************************************************************* +** +** Function l2c_ucd_enqueue_pending_out_sec_q +** +** Description enqueue outgoing UCD packet into security pending queue +** and check congestion +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data) +{ + GKI_enqueue (&p_ccb->p_lcb->ucd_out_sec_pending_q, p_data); + l2cu_check_channel_congestion (p_ccb); +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_out_sec_q +** +** Description check outgoing security +** +** Return TRUE if any UCD packet for security +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + UINT8 *p; + UINT16 psm; + BT_HDR *p_buf; + + if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count ) + { + p_buf = (BT_HDR*)(p_ccb->p_lcb->ucd_out_sec_pending_q.p_first); + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT16(psm, p) + + p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm, + p_ccb->p_lcb->handle, CONNLESS_ORIG, &l2c_link_sec_comp, p_ccb); + + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_ucd_send_pending_out_sec_q +** +** Description dequeue UCD packet from security pending queue and +** enqueue it into CCB +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + + if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count ) + { + p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_out_sec_pending_q); + + l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_buf); + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_discard_pending_out_sec_q +** +** Description dequeue UCD packet from security pending queue and +** discard it. +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + + p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_out_sec_pending_q); + + /* we may need to report to application */ + + if (p_buf) + { + GKI_freebuf (p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_check_pending_in_sec_q +** +** Description check incoming security +** +** Return TRUE if any UCD packet for security +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + UINT8 *p; + UINT16 psm; + BT_HDR *p_buf; + + if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count ) + { + p_buf = (BT_HDR*)(p_ccb->p_lcb->ucd_in_sec_pending_q.p_first); + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT16(psm, p) + + p_ccb->chnl_state = CST_TERM_W4_SEC_COMP; + btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm, + p_ccb->p_lcb->handle, CONNLESS_TERM, &l2c_link_sec_comp, p_ccb); + + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function l2c_ucd_send_pending_in_sec_q +** +** Description dequeue UCD packet from security pending queue and +** send it to application +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + + if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count ) + { + p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_in_sec_pending_q); + + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_discard_pending_in_sec_q +** +** Description dequeue UCD packet from security pending queue and +** discard it. +** +** Return None +** +*******************************************************************************/ +void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + + p_buf = (BT_HDR*)GKI_dequeue (&p_ccb->p_lcb->ucd_in_sec_pending_q); + + if (p_buf) + { + GKI_freebuf (p_buf); + } +} + +/******************************************************************************* +** +** Function l2c_ucd_check_rx_pkts +** +** Description Check if UCD reception is registered. +** Process received UCD packet if application is expecting. +** +** Return TRUE if UCD reception is registered +** +*******************************************************************************/ +BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg) +{ + tL2C_CCB *p_ccb; + tL2C_RCB *p_rcb; + + if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) != NULL) + ||((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL)) + { + if (p_ccb == NULL) + { + /* Allocate a channel control block */ + if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no CCB for UCD reception"); + GKI_freebuf (p_msg); + return TRUE; + } + else + { + /* Set CID for the connection */ + p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID; + p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID; + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT; + + /* Set the default channel priority value to use */ + l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY); + + /* Save registration info */ + p_ccb->p_rcb = p_rcb; + + p_ccb->chnl_state = CST_OPEN; + } + } + l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_msg); + return TRUE; + } + else + return FALSE; +} + +/******************************************************************************* +** +** Function l2c_ucd_process_event +** +** Description This is called from main state machine when LCID is connectionless +** Process the event if it is for UCD. +** +** Return TRUE if the event is consumed by UCD +** FALSE if the event needs to be processed by main state machine +** +*******************************************************************************/ +BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data) +{ + /* if the event is not processed by this function, this variable will be set to FALSE */ + BOOLEAN done = TRUE; + + switch (p_ccb->chnl_state) + { + case CST_CLOSED: + switch (event) + { + case L2CEVT_LP_CONNECT_CFM: /* Link came up */ + /* check if waiting for UCD info */ + if (!l2c_ucd_check_pending_info_req (p_ccb)) + { + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) + { + p_ccb->chnl_state = CST_OPEN; + } + } + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + if (!l2c_ucd_check_pending_info_req (p_ccb)) + { + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) + { + p_ccb->chnl_state = CST_OPEN; + } + } + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + case CST_ORIG_W4_SEC_COMP: + switch (event) + { + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + /* check if any outgoing UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) + { + p_ccb->chnl_state = CST_OPEN; + } + break; + + case L2CEVT_SEC_COMP: /* Security completed success */ + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_send_pending_out_sec_q(p_ccb); + + if ( p_ccb->p_lcb->ucd_out_sec_pending_q.count ) + { + /* start a timer to send next UCD packet in OPEN state */ + /* it will prevent stack overflow */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0); + } + else + { + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + } + break; + + case L2CEVT_SEC_COMP_NEG: + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_discard_pending_out_sec_q(p_ccb); + + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + + case CST_TERM_W4_SEC_COMP: + switch (event) + { + case L2CEVT_SEC_COMP: + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_send_pending_in_sec_q (p_ccb); + + if ( p_ccb->p_lcb->ucd_in_sec_pending_q.count ) + { + /* start a timer to check next UCD packet in OPEN state */ + /* it will prevent stack overflow */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0); + } + else + { + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + } + break; + + case L2CEVT_SEC_COMP_NEG: + if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) + { + done = FALSE; + break; + } + p_ccb->chnl_state = CST_OPEN; + l2c_ucd_discard_pending_in_sec_q (p_ccb); + + /* start a timer for idle timeout of UCD */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + break; + + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + break; + + case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */ + /* check if any incoming UCD packet is waiting security check */ + if (!l2c_ucd_check_pending_in_sec_q(p_ccb)) + { + p_ccb->chnl_state = CST_OPEN; + } + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + case CST_OPEN: + switch (event) + { + case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */ + /* stop idle timer of UCD */ + btu_stop_timer (&p_ccb->timer_entry); + + GKI_enqueue (&p_ccb->p_lcb->ucd_in_sec_pending_q, p_data); + l2c_ucd_check_pending_in_sec_q (p_ccb); + break; + + case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ + /* stop idle timer of UCD */ + btu_stop_timer (&p_ccb->timer_entry); + + l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data); + + /* coverity[check_return] */ /* coverity[unchecked_value] */ + /* success changes state, failure stays in current state */ + l2c_ucd_check_pending_out_sec_q (p_ccb); + break; + + case L2CEVT_TIMEOUT: + /* check if any UCD packet is waiting security check */ + if ((!l2c_ucd_check_pending_in_sec_q(p_ccb)) + &&(!l2c_ucd_check_pending_out_sec_q(p_ccb))) + { + l2cu_release_ccb (p_ccb); + } + break; + + case L2CEVT_L2CAP_INFO_RSP: + /* check if waiting for UCD info */ + l2c_ucd_check_pending_info_req (p_ccb); + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + break; + + default: + done = FALSE; /* main state machine continues to process event */ + break; + } + + return done; +} +#endif /* (L2CAP_UCD_INCLUDED == TRUE) */ diff --git a/stack/l2cap/l2c_utils.c b/stack/l2cap/l2c_utils.c new file mode 100644 index 0000000..fd618ed --- /dev/null +++ b/stack/l2cap/l2c_utils.c @@ -0,0 +1,3406 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP utility functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "l2cdefs.h" +#include "l2c_int.h" +#include "hcidefs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" + +/******************************************************************************* +** +** Function l2cu_allocate_lcb +** +** Description Look for an unused LCB +** +** Returns LCB address or NULL if none found +** +*******************************************************************************/ +tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding) +{ + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if (!p_lcb->in_use) + { + memset (p_lcb, 0, sizeof (tL2C_LCB)); + + memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN); + + p_lcb->in_use = TRUE; + p_lcb->link_state = LST_DISCONNECTED; + p_lcb->handle = HCI_INVALID_HANDLE; + p_lcb->link_flush_tout = 0xFFFF; + p_lcb->timer_entry.param = (TIMER_PARAM_TYPE)p_lcb; + p_lcb->info_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb; + p_lcb->idle_timeout = l2cb.idle_timeout; + p_lcb->id = 1; /* spec does not allow '0' */ + p_lcb->is_bonding = is_bonding; + + l2cb.num_links_active++; + + l2c_link_adjust_allocation(); + return (p_lcb); + } + } + + /* If here, no free LCB found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_update_lcb_4_bonding +** +** Description Mark the lcb for bonding. Used when bonding takes place on +** an existing ACL connection. (Pre-Lisbon devices) +** +** Returns Nothing +** +*******************************************************************************/ +void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr); + + if (p_lcb) + { + L2CAP_TRACE_DEBUG3 ("l2cu_update_lcb_4_bonding BDA: %08x%04x is_bonding: %d", + (p_bd_addr[0]<<24)+(p_bd_addr[1]<<16)+(p_bd_addr[2]<<8)+p_bd_addr[3], + (p_bd_addr[4]<<8)+p_bd_addr[5], is_bonding); + p_lcb->is_bonding = is_bonding; + } +} + +/******************************************************************************* +** +** Function l2cu_release_lcb +** +** Description Release an LCB. All timers will be stopped, channels +** dropped, buffers returned etc. +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_lcb (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + + p_lcb->in_use = FALSE; + p_lcb->is_bonding = FALSE; + + /* Stop timers */ + btu_stop_timer (&p_lcb->timer_entry); + btu_stop_timer (&p_lcb->info_timer_entry); + + /* Release any unfinished L2CAP packet on this link */ + if (p_lcb->p_hcit_rcv_acl) + { + GKI_freebuf(p_lcb->p_hcit_rcv_acl); + p_lcb->p_hcit_rcv_acl = NULL; + } + +#if BTM_SCO_INCLUDED == TRUE + /* Release all SCO links */ + btm_remove_sco_links(p_lcb->remote_bd_addr); +#endif + +#if (BLE_INCLUDED == TRUE) + p_lcb->is_ble_link = FALSE; + l2cb.is_ble_connecting = FALSE; +#endif + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + { + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + { + if (p_lcb->p_fixed_ccbs[xx]) + { + l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]); + p_lcb->p_fixed_ccbs[xx] = NULL; + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason); + } + else if ( (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL))) + && (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) ) + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason); + } + } +#endif + + /* Ensure no CCBs left on this LCB */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_lcb->ccb_queue.p_first_ccb) + { + l2cu_release_ccb (p_ccb); + } + + /* Tell BTM Acl management the link was removed */ + if ((p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_DISCONNECTING)) + btm_acl_removed (p_lcb->remote_bd_addr); + + /* Release any held buffers */ + while (p_lcb->link_xmit_data_q.p_first) + GKI_freebuf (GKI_dequeue (&p_lcb->link_xmit_data_q)); + +#if (L2CAP_UCD_INCLUDED == TRUE) + /* clean up any security pending UCD */ + l2c_ucd_delete_sec_pending_q(p_lcb); +#endif + + /* Re-adjust flow control windows make sure it does not go negative */ + if (l2cb.num_links_active >= 1) + l2cb.num_links_active--; + + l2c_link_adjust_allocation(); + + /* Check for ping outstanding */ + if (p_lcb->p_echo_rsp_cb) + { + tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; + + /* Zero out the callback in case app immediately calls us again */ + p_lcb->p_echo_rsp_cb = NULL; + + (*p_cb) (L2CAP_PING_RESULT_NO_LINK); + } +} + + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_bd_addr +** +** Description Look through all active LCBs for a match based on the +** remote BD address. +** +** Returns pointer to matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr) +{ + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (!memcmp (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN))) + { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_get_conn_role +** +** Description Determine the desired role (master or slave) of a link. +** If already got a slave link, this one must be a master. If +** already got at least 1 link where we are the master, make this +** also a master. +** +** Returns HCI_ROLE_MASTER or HCI_ROLE_SLAVE +** +*******************************************************************************/ +UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb) +{ + return l2cb.desire_role; +} + +/******************************************************************************* +** +** Function l2cu_build_header +** +** Description Builds the L2CAP command packet header +** +** Returns Pointer to allocated packet or NULL if no resources +** +*******************************************************************************/ +BT_HDR *l2cu_build_header (tL2C_LCB *p_lcb, UINT16 len, UINT8 cmd, UINT8 id) +{ + BT_HDR *p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID); + UINT8 *p; + + if (!p_buf) + { + L2CAP_TRACE_ERROR0 ("l2cu_build_header - no buffer"); + return (NULL); + } + + p_buf->offset = L2CAP_SEND_CMD_OFFSET; + p_buf->len = len + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET; + + /* Put in HCI header - handle + pkt boundary */ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + UINT16_TO_STREAM (p, p_lcb->handle | l2cb.non_flushable_pbf); +#else + UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT))); +#endif + + UINT16_TO_STREAM (p, len + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD); + UINT16_TO_STREAM (p, len + L2CAP_CMD_OVERHEAD); + +#if (BLE_INCLUDED == TRUE) + if (p_lcb->is_ble_link) + { + UINT16_TO_STREAM (p, L2CAP_BLE_SIGNALLING_CID); + } + else +#endif + { + UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID); + } + + /* Put in L2CAP command header */ + UINT8_TO_STREAM (p, cmd); + UINT8_TO_STREAM (p, id); + UINT16_TO_STREAM (p, len); + + return (p_buf); +} + +/******************************************************************************* +** +** Function l2cu_adj_id +** +** Description Checks for valid ID based on specified mask +** and adjusts the id if invalid. +** +** Returns void +** +*******************************************************************************/ +void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask) +{ +#if (L2CAP_ENHANCED_FEATURES != 0) + if ((adj_mask & L2CAP_ADJ_BRCM_ID) && p_lcb->id == L2CAP_FEATURE_REQ_ID) + { + p_lcb->id++; + } +#endif + + if ((adj_mask & L2CAP_ADJ_ZERO_ID) && !p_lcb->id) + { + p_lcb->id++; + } +} + +/******************************************************************************* +** +** Function l2cu_send_peer_cmd_reject +** +** Description Build and send an L2CAP "command reject" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id, + UINT16 p1, UINT16 p2) +{ + UINT16 param_len; + BT_HDR *p_buf; + UINT8 *p; + + /* Put in L2CAP packet header */ + if (reason == L2CAP_CMD_REJ_MTU_EXCEEDED) + param_len = 2; + else if (reason == L2CAP_CMD_REJ_INVALID_CID) + param_len = 4; + else + param_len = 0; + + if ((p_buf = l2cu_build_header (p_lcb, (UINT16) (L2CAP_CMD_REJECT_LEN + param_len), L2CAP_CMD_REJECT, rem_id)) == NULL ) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer cmd_rej"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, reason); + + if (param_len >= 2) + UINT16_TO_STREAM (p, p1); + + if (param_len >= 4) + UINT16_TO_STREAM (p, p2); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_connect_req +** +** Description Build and send an L2CAP "connection request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, L2CAP_CONN_REQ_LEN, L2CAP_CMD_CONN_REQ, + p_ccb->local_id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm); + UINT16_TO_STREAM (p, p_ccb->local_cid); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_connect_rsp +** +** Description Build and send an L2CAP "connection response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status) +{ + BT_HDR *p_buf; + UINT8 *p; + + if (result == L2CAP_CONN_PENDING) + { + /* if we already sent pending response */ + if (p_ccb->flags & CCB_FLAG_SENT_PENDING) + return; + else + p_ccb->flags |= CCB_FLAG_SENT_PENDING; + } + + if ((p_buf=l2cu_build_header(p_ccb->p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, p_ccb->remote_id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->local_cid); + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, result); + UINT16_TO_STREAM (p, status); + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_reject_connection +** +** Description Build and send an L2CAP "connection response neg" message +** to the peer. This function is called when there is no peer +** CCB (non-existant PSM or no resources). +** +** Returns void +** +*******************************************************************************/ +void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, rem_id)) == NULL ) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, 0); /* Local CID of 0 */ + UINT16_TO_STREAM (p, remote_cid); + UINT16_TO_STREAM (p, result); + UINT16_TO_STREAM (p, 0); /* Status of 0 */ + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_req +** +** Description Build and send an L2CAP "configuration request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BT_HDR *p_buf; + UINT16 cfg_len=0; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if (p_cfg->mtu_present) + cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->flush_to_present) + cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->qos_present) + cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->fcr_present) + cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->fcs_present) + cfg_len += L2CAP_CFG_FCS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->ext_flow_spec_present) + cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16) (L2CAP_CONFIG_REQ_LEN + cfg_len), + L2CAP_CMD_CONFIG_REQ, p_ccb->local_id)) == NULL ) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) */ + + /* Now, put the options */ + if (p_cfg->mtu_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU); + UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->mtu); + } + if (p_cfg->flush_to_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT); + UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->flush_to); + } + if (p_cfg->qos_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS); + UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->qos.qos_flags); + UINT8_TO_STREAM (p, p_cfg->qos.service_type); + UINT32_TO_STREAM (p, p_cfg->qos.token_rate); + UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size); + UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth); + UINT32_TO_STREAM (p, p_cfg->qos.latency); + UINT32_TO_STREAM (p, p_cfg->qos.delay_variation); + } + if (p_cfg->fcr_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR); + UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcr.mode); + UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz); + UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit); + UINT16_TO_STREAM (p, p_cfg->fcr.rtrans_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mon_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mps); + } + + if (p_cfg->fcs_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCS); + UINT8_TO_STREAM (p, L2CAP_CFG_FCS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcs); + } + + if (p_cfg->ext_flow_spec_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW); + UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype); + UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout); + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_rsp +** +** Description Build and send an L2CAP "configuration response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BT_HDR *p_buf; + UINT16 cfg_len = 0; + UINT8 *p; + + /* Create an identifier for this packet */ + if (p_cfg->mtu_present) + cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->flush_to_present) + cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->qos_present) + cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->fcr_present) + cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + if (p_cfg->ext_flow_spec_present) + cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD; + + if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16)(L2CAP_CONFIG_RSP_LEN + cfg_len), + L2CAP_CMD_CONFIG_RSP, p_ccb->remote_id)) == NULL ) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for conn_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) Must match request */ + UINT16_TO_STREAM (p, p_cfg->result); + + /* Now, put the options */ + if (p_cfg->mtu_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU); + UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->mtu); + } + if (p_cfg->flush_to_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT); + UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN); + UINT16_TO_STREAM (p, p_cfg->flush_to); + } + if (p_cfg->qos_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS); + UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->qos.qos_flags); + UINT8_TO_STREAM (p, p_cfg->qos.service_type); + UINT32_TO_STREAM (p, p_cfg->qos.token_rate); + UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size); + UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth); + UINT32_TO_STREAM (p, p_cfg->qos.latency); + UINT32_TO_STREAM (p, p_cfg->qos.delay_variation); + } + if (p_cfg->fcr_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR); + UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->fcr.mode); + UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz); + UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit); + UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.rtrans_tout); + UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.mon_tout); + UINT16_TO_STREAM (p, p_cfg->fcr.mps); + } + + if (p_cfg->ext_flow_spec_present) + { + UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW); + UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id); + UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype); + UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency); + UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout); + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_config_rej +** +** Description Build and send an L2CAP "configuration reject" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len) +{ + BT_HDR *p_buf = (BT_HDR *)GKI_getpoolbuf (L2CAP_CMD_POOL_ID); + UINT16 len, cfg_len; + UINT8 *p, *p_hci_len, *p_data_end; + UINT8 cfg_code; + + if (!p_buf) + { + L2CAP_TRACE_ERROR0 ("L2CAP - no buffer for cfg_rej"); + return; + } + + p_buf->offset = L2CAP_SEND_CMD_OFFSET; + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET; + + /* Put in HCI header - handle + pkt boundary */ +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + if (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ())) + { + UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT))); + } + else +#endif + { + UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT))); + } + + /* Remember the HCI header length position, and save space for it */ + p_hci_len = p; + p += 2; + + /* Put in L2CAP packet header */ + UINT16_TO_STREAM (p, L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len); + UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID); + + /* Put in L2CAP command header */ + UINT8_TO_STREAM (p, L2CAP_CMD_CONFIG_RSP); + UINT8_TO_STREAM (p, p_ccb->remote_id); + + UINT16_TO_STREAM (p, L2CAP_CONFIG_RSP_LEN + rej_len); + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, 0); /* Flags = 0 (no continuation) */ + UINT16_TO_STREAM (p, L2CAP_CFG_UNKNOWN_OPTIONS); + + /* Now, put the rejected options */ + p_data_end = p_data + data_len; + while (p_data < p_data_end) + { + cfg_code = *p_data; + cfg_len = *(p_data + 1); + + switch (cfg_code & 0x7F) + { + /* skip known options */ + case L2CAP_CFG_TYPE_MTU: + case L2CAP_CFG_TYPE_FLUSH_TOUT: + case L2CAP_CFG_TYPE_QOS: + p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + break; + + /* unknown options; copy into rsp if not hints */ + default: + /* sanity check option length */ + if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= data_len) + { + if ((cfg_code & 0x80) == 0) + { + memcpy(p, p_data, cfg_len + L2CAP_CFG_OPTION_OVERHEAD); + p += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + } + p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; + } + /* bad length; force loop exit */ + else + { + p_data = p_data_end; + } + break; + } + } + + len = (UINT16) (p - p_hci_len - 2); + UINT16_TO_STREAM (p_hci_len, len); + + p_buf->len = len + 4; + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_disc_req +** +** Description Build and send an L2CAP "disconnect request" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb) +{ + BT_HDR *p_buf, *p_buf2; + UINT8 *p; + + /* Create an identifier for this packet */ + p_ccb->p_lcb->id++; + l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID); + + p_ccb->local_id = p_ccb->p_lcb->id; + + if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_DISC_REQ_LEN, L2CAP_CMD_DISC_REQ, p_ccb->local_id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for disc_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, p_ccb->remote_cid); + UINT16_TO_STREAM (p, p_ccb->local_cid); + + /* Move all queued data packets to the LCB. In FCR mode, assume the higher + layer checks that all buffers are sent before disconnecting. + */ + if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) + { + while (p_ccb->xmit_hold_q.p_first) + { + p_buf2 = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q); + l2cu_set_acl_hci_header (p_buf2, p_ccb); + l2c_link_check_send_pkts (p_ccb->p_lcb, p_ccb, p_buf2); + } + } + + l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_disc_rsp +** +** Description Build and send an L2CAP "disconnect response" message +** to the peer. +** +** This function is passed the parameters for the disconnect +** response instead of the CCB address, as it may be called +** to send a disconnect response when there is no CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, + UINT16 remote_cid) +{ + BT_HDR *p_buf; + UINT8 *p; + + if ((p_buf=l2cu_build_header(p_lcb, L2CAP_DISC_RSP_LEN, L2CAP_CMD_DISC_RSP, remote_id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for disc_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, local_cid); + UINT16_TO_STREAM (p, remote_cid); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_echo_req +** +** Description Build and send an L2CAP "echo request" message +** to the peer. Note that we do not currently allow +** data in the echo request. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len) +{ + BT_HDR *p_buf; + UINT8 *p; + + p_lcb->id++; + l2cu_adj_id(p_lcb, L2CAP_ADJ_ZERO_ID); /* check for wrap to '0' */ + + if ((p_buf = l2cu_build_header(p_lcb, (UINT16) (L2CAP_ECHO_REQ_LEN + data_len), L2CAP_CMD_ECHO_REQ, p_lcb->id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for echo_req"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + if (data_len) + { + ARRAY_TO_STREAM (p, p_data, data_len); + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_echo_rsp +** +** Description Build and send an L2CAP "echo response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT16 maxlen; + + /* Don't return data if it does not fit in ACL and L2CAP MTU */ + maxlen = (GKI_get_pool_bufsize(L2CAP_CMD_POOL_ID) > btu_cb.hcit_acl_pkt_size) ? + btu_cb.hcit_acl_data_size : (UINT16)GKI_get_pool_bufsize(L2CAP_CMD_POOL_ID); + maxlen -= (UINT16)(BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + + L2CAP_CMD_OVERHEAD + L2CAP_ECHO_RSP_LEN); + + if (data_len > maxlen) + data_len = 0; + + if ((p_buf = l2cu_build_header (p_lcb, (UINT16)(L2CAP_ECHO_RSP_LEN + data_len), L2CAP_CMD_ECHO_RSP, id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for echo_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + if (data_len) + { + ARRAY_TO_STREAM (p, p_data, data_len); + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_info_req +** +** Description Build and send an L2CAP "info request" message +** to the peer. +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type) +{ + BT_HDR *p_buf; + UINT8 *p; + + /* check for wrap and/or BRCM ID */ + p_lcb->id++; + l2cu_adj_id(p_lcb, L2CAP_ADJ_ID); + + if ((p_buf = l2cu_build_header(p_lcb, 2, L2CAP_CMD_INFO_REQ, p_lcb->id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for info_req"); + return; + } + + L2CAP_TRACE_EVENT1 ("l2cu_send_peer_info_req: type 0x%04x", info_type); + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET+HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, info_type); + + p_lcb->w4_info_rsp = TRUE; + btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + + +/******************************************************************************* +** +** Function l2cu_send_peer_info_rsp +** +** Description Build and send an L2CAP "info response" message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 info_type) +{ + BT_HDR *p_buf; + UINT8 *p; + UINT16 len = L2CAP_INFO_RSP_LEN; + +#if (L2CAP_CONFORMANCE_TESTING == TRUE) + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (l2cb.test_info_resp & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE | + L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_EXT_FLOW_SPEC | + L2CAP_EXTFEA_FIXED_CHNLS | L2CAP_EXTFEA_EXT_WINDOW | + L2CAP_EXTFEA_UCD_RECEPTION )) ) +#else + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (L2CAP_EXTFEA_SUPPORTED_MASK & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE | + L2CAP_EXTFEA_NO_CRC |L2CAP_EXTFEA_FIXED_CHNLS | + L2CAP_EXTFEA_UCD_RECEPTION )) ) +#endif + { + len += L2CAP_EXTENDED_FEATURES_ARRAY_SIZE; + } + else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) + { + len += L2CAP_FIXED_CHNL_ARRAY_SIZE; + } + else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) + { + len += L2CAP_CONNLESS_MTU_INFO_SIZE; + } + + if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_INFO_RSP, remote_id)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no buffer for info_rsp"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, info_type); + +#if (L2CAP_CONFORMANCE_TESTING == TRUE) + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (l2cb.test_info_resp & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE + | L2CAP_EXTFEA_UCD_RECEPTION )) ) +#else + if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) + && (L2CAP_EXTFEA_SUPPORTED_MASK & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE + | L2CAP_EXTFEA_UCD_RECEPTION )) ) +#endif + { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); +#if (BLE_INCLUDED == TRUE) + if (p_lcb->is_ble_link) + { + /* optional data are not added for now */ + UINT32_TO_STREAM (p, L2CAP_BLE_EXTFEA_MASK); + } + else +#endif + { +#if L2CAP_CONFORMANCE_TESTING == TRUE + UINT32_TO_STREAM (p, l2cb.test_info_resp); +#else +#if (L2CAP_NUM_FIXED_CHNLS > 0) + UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK | L2CAP_EXTFEA_FIXED_CHNLS); +#else + UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK); +#endif +#endif + } + } + else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) + { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); + memset (p, 0, L2CAP_FIXED_CHNL_ARRAY_SIZE); + + p[0] = L2CAP_FIXED_CHNL_SIG_BIT; + + if ( L2CAP_EXTFEA_SUPPORTED_MASK & L2CAP_EXTFEA_UCD_RECEPTION ) + p[0] |= L2CAP_FIXED_CHNL_CNCTLESS_BIT; + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + { + int xx; + + 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); + } +#endif + } + else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) + { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS); + UINT16_TO_STREAM (p, L2CAP_UCD_MTU); + } + else + { + UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED); /* 'not supported' */ + } + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/****************************************************************************** +** +** Function l2cu_enqueue_ccb +** +** Description queue CCB by priority. The first CCB is highest priority and +** is served at first. The CCB is queued to an LLCB or an LCB. +** +** Returns None +** +*******************************************************************************/ +void l2cu_enqueue_ccb (tL2C_CCB *p_ccb) +{ + tL2C_CCB *p_ccb1; + tL2C_CCB_Q *p_q = NULL; + + /* Find out which queue the channel is on + */ + if (p_ccb->p_lcb != NULL) + p_q = &p_ccb->p_lcb->ccb_queue; + + if ( (!p_ccb->in_use) || (p_q == NULL) ) + { + L2CAP_TRACE_ERROR3 ("l2cu_enqueue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: 0x%08x", + p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb); + return; + } + + L2CAP_TRACE_DEBUG2 ("l2cu_enqueue_ccb CID: 0x%04x priority: %d", + p_ccb->local_cid, p_ccb->ccb_priority); + + /* If the queue is empty, we go at the front */ + if (!p_q->p_first_ccb) + { + p_q->p_first_ccb = p_q->p_last_ccb = p_ccb; + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; + } + else + { + p_ccb1 = p_q->p_first_ccb; + + while (p_ccb1 != NULL) + { + /* Insert new ccb at the end of the same priority. Lower number, higher priority */ + if (p_ccb->ccb_priority < p_ccb1->ccb_priority) + { + /* Are we at the head of the queue ? */ + if (p_ccb1 == p_q->p_first_ccb) + p_q->p_first_ccb = p_ccb; + else + p_ccb1->p_prev_ccb->p_next_ccb = p_ccb; + + p_ccb->p_next_ccb = p_ccb1; + p_ccb->p_prev_ccb = p_ccb1->p_prev_ccb; + p_ccb1->p_prev_ccb = p_ccb; + break; + } + + p_ccb1 = p_ccb1->p_next_ccb; + } + + /* If we are lower then anyone in the list, we go at the end */ + if (!p_ccb1) + { + /* add new ccb at the end of the list */ + p_q->p_last_ccb->p_next_ccb = p_ccb; + + p_ccb->p_next_ccb = NULL; + p_ccb->p_prev_ccb = p_q->p_last_ccb; + p_q->p_last_ccb = p_ccb; + } + } + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* Adding CCB into round robin service table of its LCB */ + if (p_ccb->p_lcb != NULL) + { + /* if this is the first channel in this priority group */ + if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) + { + /* Set the first channel to this CCB */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb; + /* Set the next serving channel in this group to this CCB */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb; + /* Initialize quota of this priority group based on its priority */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority); + } + /* increase number of channels in this group */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb++; + } +#endif + +} + +/****************************************************************************** +** +** Function l2cu_dequeue_ccb +** +** Description dequeue CCB from a queue +** +** Returns - +** +*******************************************************************************/ +void l2cu_dequeue_ccb (tL2C_CCB *p_ccb) +{ + tL2C_CCB_Q *p_q = NULL; + + L2CAP_TRACE_DEBUG1 ("l2cu_dequeue_ccb CID: 0x%04x", p_ccb->local_cid); + + /* Find out which queue the channel is on + */ + if (p_ccb->p_lcb != NULL) + p_q = &p_ccb->p_lcb->ccb_queue; + + if ( (!p_ccb->in_use) || (p_q == NULL) || (p_q->p_first_ccb == NULL) ) + { + L2CAP_TRACE_ERROR5 ("l2cu_dequeue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: 0x%08x p_q: 0x%08x p_q->p_first_ccb: 0x%08x", + p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb, p_q, p_q ? p_q->p_first_ccb : 0); + return; + } + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* Removing CCB from round robin service table of its LCB */ + if (p_ccb->p_lcb != NULL) + { + /* decrease number of channels in this priority group */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb--; + + /* if it was the last channel in the priority group */ + if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) + { + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL; + } + else + { + /* if it is the first channel of this group */ + if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb == p_ccb ) + { + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb->p_next_ccb; + } + /* if it is the next serving channel of this group */ + if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb == p_ccb ) + { + /* simply, start serving from the first channel */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb + = p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb; + } + } + } +#endif + + if (p_ccb == p_q->p_first_ccb) + { + /* We are removing the first in a queue */ + p_q->p_first_ccb = p_ccb->p_next_ccb; + + if (p_q->p_first_ccb) + p_q->p_first_ccb->p_prev_ccb = NULL; + else + p_q->p_last_ccb = NULL; + } + else if (p_ccb == p_q->p_last_ccb) + { + /* We are removing the last in a queue */ + p_q->p_last_ccb = p_ccb->p_prev_ccb; + p_q->p_last_ccb->p_next_ccb = NULL; + } + else + { + /* In the middle of a chain. */ + p_ccb->p_prev_ccb->p_next_ccb = p_ccb->p_next_ccb; + p_ccb->p_next_ccb->p_prev_ccb = p_ccb->p_prev_ccb; + } + + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; +} + +/****************************************************************************** +** +** Function l2cu_change_pri_ccb +** +** Description +** +** Returns - +** +*******************************************************************************/ +void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority) +{ + if (p_ccb->ccb_priority != priority) + { + /* If CCB is not the only guy on the queue */ + if ( (p_ccb->p_next_ccb != NULL) || (p_ccb->p_prev_ccb != NULL) ) + { + L2CAP_TRACE_DEBUG0 ("Update CCB list in logical link"); + + /* Remove CCB from queue and re-queue it at new priority */ + l2cu_dequeue_ccb (p_ccb); + + p_ccb->ccb_priority = priority; + l2cu_enqueue_ccb (p_ccb); + } +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + else + { + /* If CCB is the only guy on the queue, no need to re-enqueue */ + /* update only round robin service data */ + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 0; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL; + + p_ccb->ccb_priority = priority; + + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb; + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority); + p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 1; + } +#endif + } +} + +/******************************************************************************* +** +** Function l2cu_allocate_ccb +** +** Description This function allocates a Channel Control Block and +** attaches it to a link control block. The local CID +** is also assigned. +** +** Returns pointer to CCB, or NULL if none +** +*******************************************************************************/ +tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid) +{ + tL2C_CCB *p_ccb; + tL2C_CCB *p_prev; + + L2CAP_TRACE_DEBUG1 ("l2cu_allocate_ccb: cid 0x%04x", cid); + + if (!l2cb.p_free_ccb_first) + return (NULL); + + /* If a CID was passed in, use that, else take the first free one */ + if (cid == 0) + { + p_ccb = l2cb.p_free_ccb_first; + l2cb.p_free_ccb_first = p_ccb->p_next_ccb; + } + else + { + p_prev = NULL; + + p_ccb = &l2cb.ccb_pool[cid - L2CAP_BASE_APPL_CID]; + + if (p_ccb == l2cb.p_free_ccb_first) + l2cb.p_free_ccb_first = p_ccb->p_next_ccb; + else + { + for (p_prev = l2cb.p_free_ccb_first; p_prev != NULL; p_prev = p_prev->p_next_ccb) + { + if (p_prev->p_next_ccb == p_ccb) + { + p_prev->p_next_ccb = p_ccb->p_next_ccb; + + if (p_ccb == l2cb.p_free_ccb_last) + l2cb.p_free_ccb_last = p_prev; + + break; + } + } + if (p_prev == NULL) + { + L2CAP_TRACE_ERROR1 ("l2cu_allocate_ccb: could not find CCB for CID 0x%04x in the free list", cid); + return NULL; + } + } + } + + p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL; + + p_ccb->in_use = TRUE; + + /* Get a CID for the connection */ + p_ccb->local_cid = L2CAP_BASE_APPL_CID + (UINT16)(p_ccb - l2cb.ccb_pool); + + p_ccb->p_lcb = p_lcb; + p_ccb->p_rcb = NULL; + + /* Set priority then insert ccb into LCB queue (if we have an LCB) */ + p_ccb->ccb_priority = L2CAP_CHNL_PRIORITY_LOW; + + if (p_lcb) + l2cu_enqueue_ccb (p_ccb); + + /* clear what peer wants to configure */ + p_ccb->peer_cfg_bits = 0; + + /* Put in default values for configuration */ + memset (&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + memset (&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + /* Put in default values for local/peer configurations */ + p_ccb->our_cfg.flush_to = p_ccb->peer_cfg.flush_to = L2CAP_DEFAULT_FLUSH_TO; + p_ccb->our_cfg.mtu = p_ccb->peer_cfg.mtu = L2CAP_DEFAULT_MTU; + p_ccb->our_cfg.qos.service_type = p_ccb->peer_cfg.qos.service_type = L2CAP_DEFAULT_SERV_TYPE; + p_ccb->our_cfg.qos.token_rate = p_ccb->peer_cfg.qos.token_rate = L2CAP_DEFAULT_TOKEN_RATE; + p_ccb->our_cfg.qos.token_bucket_size = p_ccb->peer_cfg.qos.token_bucket_size = L2CAP_DEFAULT_BUCKET_SIZE; + p_ccb->our_cfg.qos.peak_bandwidth = p_ccb->peer_cfg.qos.peak_bandwidth = L2CAP_DEFAULT_PEAK_BANDWIDTH; + p_ccb->our_cfg.qos.latency = p_ccb->peer_cfg.qos.latency = L2CAP_DEFAULT_LATENCY; + p_ccb->our_cfg.qos.delay_variation = p_ccb->peer_cfg.qos.delay_variation = L2CAP_DEFAULT_DELAY; + + p_ccb->bypass_fcs = 0; + memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO)); + p_ccb->peer_cfg_already_rejected = FALSE; + p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES; + p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb; + + /* if timer is running, remove it from timer list */ + if (p_ccb->fcrb.ack_timer.in_use) + btu_stop_quick_timer (&p_ccb->fcrb.ack_timer); + + p_ccb->fcrb.mon_retrans_timer.param = (TIMER_PARAM_TYPE)p_ccb; + +// btla-specific ++ + /* CSP408639 Fix: When L2CAP send amp move channel request or receive + * L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move + * request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */ + if (p_ccb->fcrb.mon_retrans_timer.in_use) + btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer); +// btla-specific -- + + l2c_fcr_stop_timer (p_ccb); + + p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */ + p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC; /* Default mode for channel is basic mode */ + p_ccb->ertm_info.fcr_rx_pool_id = L2CAP_FCR_RX_POOL_ID; + p_ccb->ertm_info.fcr_tx_pool_id = L2CAP_FCR_TX_POOL_ID; + p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID; + p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID; + p_ccb->max_rx_mtu = L2CAP_MTU_SIZE; + p_ccb->tx_mps = GKI_get_pool_bufsize(HCI_ACL_POOL_ID) - 32; + + GKI_init_q (&p_ccb->xmit_hold_q); + + p_ccb->cong_sent = FALSE; + p_ccb->buff_quota = 2; /* This gets set after config */ + + /* If CCB was reserved Config_Done can already have some value */ + if (cid == 0) + p_ccb->config_done = 0; + else + { + L2CAP_TRACE_DEBUG2 ("l2cu_allocate_ccb: cid 0x%04x config_done:0x%x", cid, p_ccb->config_done); + } + + p_ccb->chnl_state = CST_CLOSED; + p_ccb->flags = 0; + p_ccb->tx_data_rate = L2CAP_CHNL_DATA_RATE_LOW; + p_ccb->rx_data_rate = L2CAP_CHNL_DATA_RATE_LOW; + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + p_ccb->is_flushable = FALSE; +#endif + + p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb; + p_ccb->timer_entry.in_use = 0; + + l2c_link_adjust_chnl_allocation (); + + return (p_ccb); +} + +/******************************************************************************* +** +** Function l2cu_start_post_bond_timer +** +** Description This function starts the ACL Link inactivity timer after +** dedicated bonding +** This timer can be longer than the normal link inactivity +** timer for some platforms. +** +** Returns BOOLEAN - TRUE if idle timer started or disconnect initiated +** FALSE if there's one or more pending CCB's exist +** +*******************************************************************************/ +BOOLEAN l2cu_start_post_bond_timer (UINT16 handle) +{ + UINT16 timeout; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle); + + if (!p_lcb) + return (TRUE); + + p_lcb->is_bonding = FALSE; + + /* Only start timer if no control blocks allocated */ + if (p_lcb->ccb_queue.p_first_ccb != NULL) + return (FALSE); + + /* If no channels on the connection, start idle timeout */ + if ( (p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_DISCONNECTING) ) + { + if (p_lcb->idle_timeout == 0) + { + if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) + { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } + else + timeout = BT_1SEC_TIMEOUT; + } + else + { + timeout = L2CAP_BONDING_TIMEOUT; + } + + if (timeout != 0xFFFF) + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + + return (TRUE); + } + + return (FALSE); +} + +/******************************************************************************* +** +** Function l2cu_release_ccb +** +** Description This function releases a Channel Control Block. The timer +** is stopped, any attached buffers freed, and the CCB is removed +** from the link control block. +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_ccb (tL2C_CCB *p_ccb) +{ + tL2C_LCB *p_lcb = p_ccb->p_lcb; + tL2C_RCB *p_rcb = p_ccb->p_rcb; + + L2CAP_TRACE_DEBUG2 ("l2cu_release_ccb: cid 0x%04x in_use: %u", p_ccb->local_cid, p_ccb->in_use); + + /* If already released, could be race condition */ + if (!p_ccb->in_use) + return; + + if (p_rcb && (p_rcb->psm != p_rcb->real_psm)) + { + btm_sec_clr_service_by_psm(p_rcb->psm); + } + /* Stop the timer */ + btu_stop_timer (&p_ccb->timer_entry); + + while (p_ccb->xmit_hold_q.p_first) + GKI_freebuf (GKI_dequeue (&p_ccb->xmit_hold_q)); + + l2c_fcr_cleanup (p_ccb); + + /* Channel may not be assigned to any LCB if it was just pre-reserved */ + if ( (p_lcb) && + ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) +#if (L2CAP_UCD_INCLUDED == TRUE) + ||(p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID) +#endif + ) + ) + { + l2cu_dequeue_ccb (p_ccb); + + /* Delink the CCB from the LCB */ + p_ccb->p_lcb = NULL; + } + + /* Put the CCB back on the free pool */ + if (!l2cb.p_free_ccb_first) + { + l2cb.p_free_ccb_first = p_ccb; + l2cb.p_free_ccb_last = p_ccb; + p_ccb->p_next_ccb = NULL; + p_ccb->p_prev_ccb = NULL; + } + else + { + p_ccb->p_next_ccb = NULL; + p_ccb->p_prev_ccb = l2cb.p_free_ccb_last; + l2cb.p_free_ccb_last->p_next_ccb = p_ccb; + l2cb.p_free_ccb_last = p_ccb; + } + + /* Flag as not in use */ + p_ccb->in_use = FALSE; + + /* If no channels on the connection, start idle timeout */ + if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED)) + { + if (!p_lcb->ccb_queue.p_first_ccb) + { + l2cu_no_dynamic_ccbs (p_lcb); + } + else + { + /* Link is still active, adjust channel quotas. */ + l2c_link_adjust_chnl_allocation (); + } + } +} + +/******************************************************************************* +** +** Function l2cu_find_ccb_by_remote_cid +** +** Description Look through all active CCBs on a link for a match based +** on the remote CID. +** +** Returns pointer to matched CCB, or NULL if no match +** +*******************************************************************************/ +tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid) +{ + tL2C_CCB *p_ccb; + + /* If LCB is NULL, look through all active links */ + if (!p_lcb) + { + return NULL; + } + else + { + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + if ((p_ccb->in_use) && (p_ccb->remote_cid == remote_cid)) + return (p_ccb); + } + + /* If here, no match found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_allocate_rcb +** +** Description Look through the Registration Control Blocks for a free +** one. +** +** Returns Pointer to the RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_allocate_rcb (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if (!p_rcb->in_use) + { + p_rcb->in_use = TRUE; + p_rcb->psm = psm; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_rcb->ucd.state = L2C_UCD_STATE_UNUSED; +#endif + return (p_rcb); + } + } + + /* If here, no free RCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function l2cu_release_rcb +** +** Description Mark an RCB as no longet in use +** +** Returns void +** +*******************************************************************************/ +void l2cu_release_rcb (tL2C_RCB *p_rcb) +{ + p_rcb->in_use = FALSE; + p_rcb->psm = 0; +} + + +/******************************************************************************* +** +** Function l2cu_disconnect_chnl +** +** Description Disconnect a channel. Typically, this is due to either +** receiving a bad configuration, bad packet or max_retries expiring. +** +*******************************************************************************/ +void l2cu_disconnect_chnl (tL2C_CCB *p_ccb) +{ + UINT16 local_cid = p_ccb->local_cid; + + if (local_cid >= L2CAP_BASE_APPL_CID) + { + tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb; + + L2CAP_TRACE_WARNING1 ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid); + + l2cu_send_peer_disc_req (p_ccb); + + l2cu_release_ccb (p_ccb); + + (*p_disc_cb)(local_cid, FALSE); + } + else + { + /* failure on the AMP channel, probably need to disconnect ACL */ + L2CAP_TRACE_ERROR1 ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid); + } +} + + +/******************************************************************************* +** +** Function l2cu_find_rcb_by_psm +** +** Description Look through the Registration Control Blocks to see if +** anyone registered to handle the PSM in question +** +** Returns Pointer to the RCB or NULL if not found +** +*******************************************************************************/ +tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm) +{ + tL2C_RCB *p_rcb = &l2cb.rcb_pool[0]; + UINT16 xx; + + for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) + { + if ((p_rcb->in_use) && (p_rcb->psm == psm)) + return (p_rcb); + } + + /* If here, no match found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function l2cu_process_peer_cfg_req +** +** Description This function is called when the peer sends us a "config request" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Note: Negotiation of the FCR channel type is handled internally, +** all others are passed to the upper layer. +** +** Returns UINT8 - L2CAP_PEER_CFG_OK if passed to upper layer, +** L2CAP_PEER_CFG_UNACCEPTABLE if automatically responded to +** because parameters are unnacceptable from a specification +** point of view. +** L2CAP_PEER_CFG_DISCONNECT if no compatible channel modes +** between the two devices, and shall be closed. +** +*******************************************************************************/ +UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + BOOLEAN mtu_ok = TRUE; + BOOLEAN qos_type_ok = TRUE; + BOOLEAN flush_to_ok = TRUE; + BOOLEAN fcr_ok = TRUE; + UINT8 fcr_status; + + /* Ignore FCR parameters for basic mode */ + if (!p_cfg->fcr_present) + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + + /* Save the MTU that our peer can receive */ + if (p_cfg->mtu_present) + { + /* Make sure MTU is at least the minimum */ + if (p_cfg->mtu >= L2CAP_MIN_MTU) + { + /* In basic mode, limit the MTU to our buffer size */ + if ( (p_cfg->fcr_present == FALSE) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) + p_cfg->mtu = L2CAP_MTU_SIZE; + + /* Save the accepted value in case of renegotiation */ + p_ccb->peer_cfg.mtu = p_cfg->mtu; + p_ccb->peer_cfg.mtu_present = TRUE; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_MTU; + } + else /* Illegal MTU value */ + { + p_cfg->mtu = L2CAP_MIN_MTU; + mtu_ok = FALSE; + } + } + /* Reload mtu from a previously accepted config request */ + else if (p_ccb->peer_cfg.mtu_present) + { + p_cfg->mtu_present = TRUE; + p_cfg->mtu = p_ccb->peer_cfg.mtu; + } + + /* Verify that the flush timeout is a valid value (0 is illegal) */ + if (p_cfg->flush_to_present) + { + if (!p_cfg->flush_to) + { + p_cfg->flush_to = 0xFFFF; /* Infinite retransmissions (spec default) */ + flush_to_ok = FALSE; + } + else /* Save the accepted value in case of renegotiation */ + { + p_ccb->peer_cfg.flush_to_present = TRUE; + p_ccb->peer_cfg.flush_to = p_cfg->flush_to; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO; + } + } + /* Reload flush_to from a previously accepted config request */ + else if (p_ccb->peer_cfg.flush_to_present) + { + p_cfg->flush_to_present = TRUE; + p_cfg->flush_to = p_ccb->peer_cfg.flush_to; + } + + /* Save the QOS settings the the peer is using */ + if (p_cfg->qos_present) + { + /* Make sure service type is not a reserved value; otherwise let upper + layer decide if acceptable + */ + if (p_cfg->qos.service_type <= GUARANTEED) + { + p_ccb->peer_cfg.qos = p_cfg->qos; + p_ccb->peer_cfg.qos_present = TRUE; + p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS; + } + else /* Illegal service type value */ + { + p_cfg->qos.service_type = BEST_EFFORT; + qos_type_ok = FALSE; + } + } + /* Reload QOS from a previously accepted config request */ + else if (p_ccb->peer_cfg.qos_present) + { + p_cfg->qos_present = TRUE; + p_cfg->qos = p_ccb->peer_cfg.qos; + } + + if ((fcr_status = l2c_fcr_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_DISCONNECT) + { + /* Notify caller to disconnect the channel (incompatible modes) */ + p_cfg->result = L2CAP_CFG_FAILED_NO_REASON; + p_cfg->mtu_present = p_cfg->qos_present = p_cfg->flush_to_present = 0; + + return (L2CAP_PEER_CFG_DISCONNECT); + } + + fcr_ok = (fcr_status == L2CAP_PEER_CFG_OK); + + /* Return any unacceptable parameters */ + if (mtu_ok && flush_to_ok && qos_type_ok && fcr_ok) + { + l2cu_adjust_out_mps (p_ccb); + return (L2CAP_PEER_CFG_OK); + } + else + { + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + + if (mtu_ok) + p_cfg->mtu_present = FALSE; + if (flush_to_ok) + p_cfg->flush_to_present = FALSE; + if (qos_type_ok) + p_cfg->qos_present = FALSE; + if (fcr_ok) + p_cfg->fcr_present = FALSE; + + return (L2CAP_PEER_CFG_UNACCEPTABLE); + } +} + + +/******************************************************************************* +** +** Function l2cu_process_peer_cfg_rsp +** +** Description This function is called when the peer sends us a "config response" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + /* If we wanted QoS and the peer sends us a positive response with QoS, use his values */ + if ( (p_cfg->qos_present) && (p_ccb->our_cfg.qos_present) ) + p_ccb->our_cfg.qos = p_cfg->qos; + + if (p_cfg->fcr_present) + { + /* Save the retransmission and monitor timeout values */ + if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE) + { + p_ccb->peer_cfg.fcr.rtrans_tout = p_cfg->fcr.rtrans_tout; + p_ccb->peer_cfg.fcr.mon_tout = p_cfg->fcr.mon_tout; + } + + /* Calculate the max number of packets for which we can delay sending an ack */ + if (p_cfg->fcr.tx_win_sz < p_ccb->our_cfg.fcr.tx_win_sz) + p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3; + else + p_ccb->fcrb.max_held_acks = p_ccb->our_cfg.fcr.tx_win_sz / 3; + + L2CAP_TRACE_DEBUG3 ("l2cu_process_peer_cfg_rsp(): peer tx_win_sz: %d, our tx_win_sz: %d, max_held_acks: %d", + p_cfg->fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz, p_ccb->fcrb.max_held_acks); + } +} + +/******************************************************************************* +** +** Function l2cu_process_our_cfg_req +** +** Description This function is called when we send a "config request" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + tL2C_LCB *p_lcb; + UINT16 hci_flush_to; + + /* Save the QOS settings we are using for transmit */ + if (p_cfg->qos_present) + { + p_ccb->our_cfg.qos_present = TRUE; + p_ccb->our_cfg.qos = p_cfg->qos; + } + + if (p_cfg->fcr_present) + { + /* Override FCR options if attempting streaming or basic */ + if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) + memset(&p_cfg->fcr, 0, sizeof(tL2CAP_FCR_OPTS)); + else + { + /* On BR/EDR, timer values are zero in config request */ + /* On class 2 AMP, timer value in config request shall be non-0 processing time */ + /* timer value in config response shall be greater than received processing time */ + p_cfg->fcr.mon_tout = p_cfg->fcr.rtrans_tout = 0; + + if (p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE) + p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0; + } + + /* Set the threshold to send acks (may be updated in the cfg response) */ + p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3; + + /* Include FCS option only if peer can handle it */ + if (p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_NO_CRC) + { + /* FCS check can be bypassed if peer also desires to bypass */ + if (p_cfg->fcs_present && p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) + p_ccb->bypass_fcs |= L2CAP_CFG_FCS_OUR; + } + else + p_cfg->fcs_present = FALSE; + } + else + { + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + } + + p_ccb->our_cfg.fcr.mode = p_cfg->fcr.mode; + p_ccb->our_cfg.fcr_present = p_cfg->fcr_present; + + /* Check the flush timeout. If it is lower than the current one used */ + /* then we need to adjust the flush timeout sent to the controller */ + if (p_cfg->flush_to_present) + { + if ((p_cfg->flush_to == 0)||(p_cfg->flush_to == L2CAP_NO_AUTOMATIC_FLUSH)) + { + /* don't send invalid flush timeout */ + /* SPEC: The sender of the Request shall specify its flush timeout value */ + /* if it differs from the default value of 0xFFFF */ + p_cfg->flush_to_present = FALSE; + } + else + { + p_ccb->our_cfg.flush_to = p_cfg->flush_to; + p_lcb = p_ccb->p_lcb; + + if (p_cfg->flush_to < p_lcb->link_flush_tout) + { + p_lcb->link_flush_tout = p_cfg->flush_to; + + /* If the timeout is within range of HCI, set the flush timeout */ + if (p_cfg->flush_to <= ((HCI_MAX_AUTO_FLUSH_TOUT * 5) / 8)) + { + /* Convert flush timeout to 0.625 ms units, with round */ + hci_flush_to = ((p_cfg->flush_to * 8) + 3) / 5; + btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to); + } + } + } + } +} + + +/******************************************************************************* +** +** Function l2cu_process_our_cfg_rsp +** +** Description This function is called when we send the peer a "config response" +** message. It extracts the configuration of interest and saves +** it in the CCB. +** +** Returns void +** +*******************************************************************************/ +void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg) +{ + /* If peer wants QoS, we are allowed to change the values in a positive response */ + if ( (p_cfg->qos_present) && (p_ccb->peer_cfg.qos_present) ) + p_ccb->peer_cfg.qos = p_cfg->qos; + else + p_cfg->qos_present = FALSE; + + l2c_fcr_adj_our_rsp_options (p_ccb, p_cfg); +} + + +/******************************************************************************* +** +** Function l2cu_device_reset +** +** Description This function is called when reset of the device is +** completed. For all active connection simulate HCI_DISC +** +** Returns void +** +*******************************************************************************/ +void l2cu_device_reset (void) +{ + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->handle != HCI_INVALID_HANDLE)) + { + l2c_link_hci_disc_comp (p_lcb->handle, (UINT8) -1); + } + } +} + +#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE) +extern UINT16 tcs_wug_get_clk_offset( BD_ADDR addr ) ; +#endif + +/******************************************************************************* +** +** Function l2cu_create_conn +** +** Description This function initiates an acl connection via HCI +** +** Returns TRUE if successful, FALSE if gki get buffer fails. +** +*******************************************************************************/ +BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb) +{ + int xx; + tL2C_LCB *p_lcb_cur = &l2cb.lcb_pool[0]; +#if BTM_SCO_INCLUDED == TRUE + BOOLEAN is_sco_active; +#endif + +#if (BLE_INCLUDED == TRUE) + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; + + BTM_ReadDevInfo(p_lcb->remote_bd_addr, &dev_type, &addr_type); + + if (dev_type == BT_DEVICE_TYPE_BLE) + { + p_lcb->ble_addr_type = addr_type; + p_lcb->is_ble_link = TRUE; + + return (l2cble_create_conn(p_lcb)); + } +#endif + + /* If there is a connection where we perform as a slave, try to switch roles + for this connection */ + for (xx = 0, p_lcb_cur = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb_cur++) + { + if (p_lcb_cur == p_lcb) + continue; + + if ((p_lcb_cur->in_use) && (p_lcb_cur->link_role == HCI_ROLE_SLAVE)) + { + +#if BTM_SCO_INCLUDED == TRUE + /* The LMP_switch_req shall be sent only if the ACL logical transport + is in active mode, when encryption is disabled, and all synchronous + logical transports on the same physical link are disabled." */ + + /* Check if there is any SCO Active on this BD Address */ + is_sco_active = btm_is_sco_active_by_bdaddr(p_lcb_cur->remote_bd_addr); + + L2CAP_TRACE_API1 ("l2cu_create_conn - btm_is_sco_active_by_bdaddr() is_sco_active = %s", \ + (is_sco_active == TRUE) ? "TRUE":"FALSE"); + + if (is_sco_active == TRUE) + continue; /* No Master Slave switch not allowed when SCO Active */ +#endif + + if (HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features)) + { + /* mark this lcb waiting for switch to be completed and + start switch on the other one */ + p_lcb->link_state = LST_CONNECTING_WAIT_SWITCH; + p_lcb->link_role = HCI_ROLE_MASTER; + + if (BTM_SwitchRole (p_lcb_cur->remote_bd_addr, HCI_ROLE_MASTER, NULL) == BTM_CMD_STARTED) + { + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_ROLE_SWITCH_TOUT); + return (TRUE); + } + } + } + } + + p_lcb->link_state = LST_CONNECTING; + + return (l2cu_create_conn_after_switch (p_lcb)); +} + +/******************************************************************************* +** +** Function l2cu_get_num_hi_priority +** +** Description Gets the number of high priority channels. +** +** Returns +** +*******************************************************************************/ +UINT8 l2cu_get_num_hi_priority (void) +{ + UINT8 no_hi = 0; + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) + { + no_hi++; + } + } + return no_hi; +} + + +/******************************************************************************* +** +** Function l2cu_create_conn_after_switch +** +** Description This function initiates an acl connection via HCI +** If switch required to create connection it is already done. +** +** Returns TRUE if successful, FALSE if gki get buffer fails. +** +*******************************************************************************/ + +BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb) +{ + UINT8 allow_switch = HCI_CR_CONN_ALLOW_SWITCH; + tBTM_INQ_INFO *p_inq_info; + UINT8 page_scan_rep_mode; + UINT8 page_scan_mode; + UINT16 clock_offset; + UINT8 *p_features; + UINT16 num_acl = BTM_GetNumAclLinks(); + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_lcb->remote_bd_addr); + UINT8 no_hi_prio_chs = l2cu_get_num_hi_priority(); + + p_features = BTM_ReadLocalFeatures(); + + L2CAP_TRACE_DEBUG4 ("l2cu_create_conn_after_switch :%d num_acl:%d no_hi: %d is_bonding:%d", + l2cb.disallow_switch, num_acl, no_hi_prio_chs, p_lcb->is_bonding); + /* FW team says that we can participant in 4 piconets + * typically 3 piconet + 1 for scanning. + * We can enhance the code to count the number of piconets later. */ + if ( ((!l2cb.disallow_switch && (num_acl < 3)) || (p_lcb->is_bonding && (no_hi_prio_chs==0))) + && HCI_SWITCH_SUPPORTED(p_features)) + allow_switch = HCI_CR_CONN_ALLOW_SWITCH; + else + allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH; + + p_lcb->link_state = LST_CONNECTING; + + +#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE) + if ( (clock_offset = tcs_wug_get_clk_offset( p_lcb->remote_bd_addr )) != 0 ) + { + page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R0; + page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE; + } + else + { +#endif + + /* Check with the BT manager if details about remote device are known */ + if ((p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr)) != NULL) + { + page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode; + page_scan_mode = p_inq_info->results.page_scan_mode; + clock_offset = (UINT16)(p_inq_info->results.clock_offset); + } + else + { + /* No info known. Use default settings */ + page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R1; + page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE; + + clock_offset = (p_dev_rec) ? p_dev_rec->clock_offset : 0; + } +#if (TCS_WUG_MEMBER_INCLUDED == TRUE && TCS_INCLUDED == TRUE) + } +#endif + + if (!btsnd_hcic_create_conn (p_lcb->remote_bd_addr, + HCI_PKT_TYPES_MASK_DM1 + HCI_PKT_TYPES_MASK_DH1, + page_scan_rep_mode, + page_scan_mode, + clock_offset, + allow_switch)) + + { + L2CAP_TRACE_ERROR0 ("L2CAP - no buffer for l2cu_create_conn"); + l2cu_release_lcb (p_lcb); + return (FALSE); + } + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); +#endif + + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, + L2CAP_LINK_CONNECT_TOUT); + + return (TRUE); +} + + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_state +** +** Description Look through all active LCBs for a match based on the +** LCB state. +** +** Returns pointer to first matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state) +{ + UINT16 i; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (i = 0; i < MAX_L2CAP_LINKS; i++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->link_state == state)) + { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function l2cu_lcb_disconnecting +** +** Description On each active lcb, check if the lcb is in disconnecting +** state, or if there are no ccb's on the lcb (implying + idle timeout is running), or if last ccb on the link + is in disconnecting state. +** +** Returns TRUE if any of above conditions met, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN l2cu_lcb_disconnecting (void) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + UINT16 i; + BOOLEAN status = FALSE; + + p_lcb = &l2cb.lcb_pool[0]; + + for (i = 0; i < MAX_L2CAP_LINKS; i++, p_lcb++) + { + if (p_lcb->in_use) + { + /* no ccbs on lcb, or lcb is in disconnecting state */ + if ((!p_lcb->ccb_queue.p_first_ccb) || (p_lcb->link_state == LST_DISCONNECTING)) + { + status = TRUE; + break; + } + /* only one ccb left on lcb */ + else if (p_lcb->ccb_queue.p_first_ccb == p_lcb->ccb_queue.p_last_ccb) + { + p_ccb = p_lcb->ccb_queue.p_first_ccb; + + if ((p_ccb->in_use) && + ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) || + (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) + { + status = TRUE; + break; + } + } + } + } + return status; +} + + +/******************************************************************************* +** +** Function l2cu_set_acl_priority +** +** Description Sets the transmission priority for a channel. +** (For initial implementation only two values are valid. +** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH). +** +** Returns TRUE if a valid channel, else FALSE +** +*******************************************************************************/ + +BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs) +{ + tL2C_LCB *p_lcb; + UINT8 *pp; + UINT8 command[HCI_BRCM_ACL_PRIORITY_PARAM_SIZE]; + UINT8 vs_param; + + APPL_TRACE_EVENT1("SET ACL PRIORITY %d", priority); + + /* Find the link control block for the acl channel */ + if ((p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr)) == NULL) + { + L2CAP_TRACE_WARNING0 ("L2CAP - no LCB for L2CA_SetAclPriority"); + return (FALSE); + } + + if (BTM_IS_BRCM_CONTROLLER()) + { + /* Called from above L2CAP through API; send VSC if changed */ + if ((!reset_after_rs && (priority != p_lcb->acl_priority)) || + /* Called because of a master/slave role switch; if high resend VSC */ + ( reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) + { + pp = command; + + vs_param = (priority == L2CAP_PRIORITY_HIGH) ? HCI_BRCM_ACL_PRIORITY_HIGH : HCI_BRCM_ACL_PRIORITY_LOW; + + UINT16_TO_STREAM (pp, p_lcb->handle); + UINT8_TO_STREAM (pp, vs_param); + + BTM_VendorSpecificCommand (HCI_BRCM_SET_ACL_PRIORITY, HCI_BRCM_ACL_PRIORITY_PARAM_SIZE, command, NULL); + + /* Adjust lmp buffer allocation for this channel if priority changed */ + if (p_lcb->acl_priority != priority) + { + p_lcb->acl_priority = priority; + l2c_link_adjust_allocation(); + } + } + } + return(TRUE); +} + +#if (L2CAP_ENHANCED_FEATURES != 0) +/******************************************************************************* +** +** Function l2cu_send_feature_req +** +** Description Called at connection establishment by the originator +** of the connection to send an L2CAP Echo request message +** to the peer to determine if he supports Widcomm proprietary +** features. +** +** Returns void +** +*******************************************************************************/ +void l2cu_send_feature_req (tL2C_CCB *p_ccb) +{ + UINT8 saved_id; + UINT8 buff[100], *p = buff; + + UINT8_TO_STREAM (p, 'R'); + UINT8_TO_STREAM (p, 'Q'); + + UINT8_TO_STREAM (p, 'r'); + UINT8_TO_STREAM (p, 'q'); + + /* save current ID to be restored after feature request */ + saved_id = p_ccb->p_lcb->id; + + /* Set appropriate ID */ + p_ccb->p_lcb->id = L2CAP_FEATURE_REQ_ID - 1; + + l2cu_send_peer_echo_req (p_ccb->p_lcb, buff, (UINT16)(p - buff)); + + /* packet has been built so we can restore the control block id */ + p_ccb->p_lcb->id = saved_id; +} + + + +/******************************************************************************* +** +** Function l2cu_check_feature_req +** +** Description Called when an echo request is received to check if the +** other side is doing a proprietary feature request. If so, +** extract the values and reply with a features response. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len) +{ + UINT8 buff[100]; + UINT8 *p_out = buff; + UINT8 *p_end = p_data + data_len - 2; + UINT8 pe_type, pe_len; + + if ((data_len <= 4) + || (p_data[0] != 'R') + || (p_data[1] != 'Q') + || (p_data[data_len - 2] != 'r') + || (p_data[data_len - 1] != 'q') + || (id != L2CAP_FEATURE_REQ_ID)) + return (FALSE); + + /* Skip leading frame characters */ + p_data += 2; + + UINT8_TO_STREAM (p_out, 'R'); + UINT8_TO_STREAM (p_out, 'S'); + + while (p_data < p_end) + { + pe_type = *p_data++; + pe_len = *p_data++; + + switch (pe_type) + { + default: + p_data += pe_len; + break; + } + } + + /* Sanity check - we should not overrun the input */ + if (p_data != p_end) + { + L2CAP_TRACE_ERROR0 ("L2CAP - badly formatted feature req"); + return (FALSE); + } + + UINT8_TO_STREAM (p_out, 'r'); + UINT8_TO_STREAM (p_out, 's'); + + l2cu_send_peer_echo_rsp (p_lcb, L2CAP_FEATURE_RSP_ID, buff, (UINT16)(p_out - buff)); + + return (TRUE); +} + +/******************************************************************************* +** +** Function l2cu_check_feature_rsp +** +** Description Called when an echo response is received to check if the +** other side is suports proprietary feature(s). If so, +** extract the values. +** +** Returns void +** +*******************************************************************************/ +void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len) +{ + UINT8 *p_end = p_data + data_len - 2; + + if ((data_len <= 4) + || (p_data[0] != 'R') + || (p_data[1] != 'S') + || (p_data[data_len - 2] != 'r') + || (p_data[data_len - 1] != 's') + || (id != L2CAP_FEATURE_RSP_ID)) + { + return; + } + + /* Skip leading frame characters */ + p_data += 2; + + while (p_data < p_end) + { + UINT8 pe_id = *p_data++; + UINT8 pe_len = *p_data++; + + switch (pe_id) + { + default: + p_data += pe_len; + break; + } + } + + /* Sanity check - we should not overrun the input */ + if (p_data != p_end) + { + L2CAP_TRACE_ERROR0 ("L2CAP - badly formatted feature rsp"); + } +} +#endif /* L2CAP_ENHANCED_FEATURES != 0 */ + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) +/****************************************************************************** +** +** Function l2cu_set_non_flushable_pbf +** +** Description set L2CAP_PKT_START_NON_FLUSHABLE if controller supoorts +** +** Returns void +** +*******************************************************************************/ +void l2cu_set_non_flushable_pbf (BOOLEAN is_supported) +{ + if (is_supported) + l2cb.non_flushable_pbf = (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT); + else + l2cb.non_flushable_pbf = (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT); +} +#endif + +/******************************************************************************* +** +** Function l2cu_resubmit_pending_sec_req +** +** Description This function is called when required security procedures +** are completed and any pending requests can be re-submitted. +** +** Returns void +** +*******************************************************************************/ +void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda) +{ + tL2C_LCB *p_lcb; + tL2C_CCB *p_ccb; + tL2C_CCB *p_next_ccb; + int xx; + + L2CAP_TRACE_DEBUG1 ("l2cu_resubmit_pending_sec_req p_bda: 0x%08x", p_bda); + + /* If we are called with a BDA, only resubmit for that BDA */ + if (p_bda) + { + p_lcb = l2cu_find_lcb_by_bd_addr (p_bda); + + /* If we don't have one, this is an error */ + if (p_lcb) + { + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) + { + p_next_ccb = p_ccb->p_next_ccb; + l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL); + } + } + else + { + L2CAP_TRACE_WARNING0 ("l2cu_resubmit_pending_sec_req - unknown BD_ADDR"); + } + } + else + { + /* No BDA pasesed in, so check all links */ + for (xx = 0, p_lcb = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if (p_lcb->in_use) + { + /* For all channels, send the event through their FSMs */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) + { + p_next_ccb = p_ccb->p_next_ccb; + l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL); + } + } + } + } +} + +#if L2CAP_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function l2cu_set_info_rsp_mask +** +** Description This function allows the script wrapper to change the +** info resp mask for conformance testing. +** +** Returns pointer to CCB, or NULL if none +** +*******************************************************************************/ +void l2cu_set_info_rsp_mask (UINT32 mask) +{ + l2cb.test_info_resp = mask; +} +#endif /* L2CAP_CONFORMANCE_TESTING */ + +/******************************************************************************* +** +** Function l2cu_adjust_out_mps +** +** Description Sets our MPS based on current controller capabilities +** +** Returns void +** +*******************************************************************************/ +void l2cu_adjust_out_mps (tL2C_CCB *p_ccb) +{ + UINT16 packet_size; + + /* on the tx side MTU is selected based on packet size of the controller */ + packet_size = btm_get_max_packet_size (p_ccb->p_lcb->remote_bd_addr); + + if (packet_size <= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN)) + { + /* something is very wrong */ + L2CAP_TRACE_ERROR2 ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps); + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps; + } + else + { + packet_size -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN); + + /* We try to negotiate MTU that each packet can be split into whole + number of max packets. For example if link is 1.2 max packet size is 339 bytes. + At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead. + 1695, that will be 5 Dh5 packets. Now maximum L2CAP packet is + 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. + + For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet + 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. */ + if (p_ccb->peer_cfg.fcr.mps >= packet_size) + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps / packet_size * packet_size; + else + p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps; + + L2CAP_TRACE_DEBUG3 ("l2cu_adjust_out_mps use %d Based on peer_cfg.fcr.mps: %u packet_size: %u", + p_ccb->tx_mps, p_ccb->peer_cfg.fcr.mps, packet_size); + } +} + + +/******************************************************************************* +** +** Function l2cu_initialize_fixed_ccb +** +** Description Initialize a fixed channel's CCB +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr) +{ +#if (L2CAP_NUM_FIXED_CHNLS > 0) + tL2C_CCB *p_ccb; + + /* If we already have a CCB, then simply return */ + if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL) + return (TRUE); + + if ((p_ccb = l2cu_allocate_ccb (NULL, 0)) == NULL) + return (FALSE); + + /* Set CID for the connection */ + p_ccb->local_cid = fixed_cid; + p_ccb->remote_cid = fixed_cid; + + GKI_init_q (&p_ccb->xmit_hold_q); + + p_ccb->is_flushable = FALSE; + + p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb; + + + if (p_fcr) + { + /* Set the FCR parameters. For now, we will use default pools */ + p_ccb->our_cfg.fcr = p_ccb->peer_cfg.fcr = *p_fcr; + + p_ccb->ertm_info.fcr_rx_pool_id = HCI_ACL_POOL_ID; + p_ccb->ertm_info.fcr_tx_pool_id = HCI_ACL_POOL_ID; + p_ccb->ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID; + p_ccb->ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID; + + p_ccb->fcrb.max_held_acks = p_fcr->tx_win_sz / 3; + } + + /* Link ccb to lcb and lcb to ccb */ + p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = p_ccb; + p_ccb->p_lcb = p_lcb; + + /* There is no configuration, so if the link is up, the channel is up */ + if (p_lcb->link_state == LST_CONNECTED) + p_ccb->chnl_state = CST_OPEN; + + /* Set the default idle timeout value to use */ + p_ccb->fixed_chnl_idle_tout = l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].default_idle_tout; +#endif + return (TRUE); +} + +/******************************************************************************* +** +** Function l2cu_no_dynamic_ccbs +** +** Description Handles the case when there are no more dynamic CCBs. If there +** are any fixed CCBs, start the longest of the fixed CCB timeouts, +** otherwise start the default link idle timeout or disconnect. +** +** Returns void +** +*******************************************************************************/ +void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb) +{ + tBTM_STATUS rc; + UINT16 timeout = p_lcb->idle_timeout; + +#if (L2CAP_NUM_FIXED_CHNLS > 0) + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + { + if ( (p_lcb->p_fixed_ccbs[xx] != NULL) && (p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout > timeout) ) + timeout = p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout; + } +#endif + + /* If the link is pairing, do not mess with the timeouts */ + if (p_lcb->is_bonding) + return; + + if (timeout == 0) + { + L2CAP_TRACE_DEBUG0 ("l2cu_no_dynamic_ccbs() IDLE timer 0, disconnecting link"); + + rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER); + if (rc == BTM_CMD_STARTED) + { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } + else if (rc == BTM_SUCCESS) + { + /* BTM SEC will make sure that link is release (probably after pairing is done) */ + p_lcb->link_state = LST_DISCONNECTING; + timeout = 0xFFFF; + } + else if ( (p_lcb->is_bonding) + && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) ) + { + p_lcb->link_state = LST_DISCONNECTING; + timeout = L2CAP_LINK_DISCONNECT_TOUT; + } + else + { + /* probably no buffer to send disconnect */ + timeout = BT_1SEC_TIMEOUT; + } + } + + if (timeout != 0xFFFF) + { + L2CAP_TRACE_DEBUG1 ("l2cu_no_dynamic_ccbs() starting IDLE timeout: %d", timeout); + btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout); + } + else + { + btu_stop_timer(&p_lcb->timer_entry); + } +} + +#if (L2CAP_NUM_FIXED_CHNLS > 0) +/******************************************************************************* +** +** Function l2cu_process_fixed_chnl_resp +** +** Description handle a fixed channel response (or lack thereof) +** if the link failed, or a fixed channel response was +** not received, the bitfield is all zeros. +** +*******************************************************************************/ +void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb) +{ + int xx; +#if BLE_INCLUDED == TRUE + UINT16 reason = (p_lcb->is_ble_link ) ? 1 : 0; +#else + UINT16 reason =0; +#endif + + /* Tell all registered fixed channels about the connection */ + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + { + 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->p_fixed_ccbs[xx]) + p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN; + + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, TRUE, reason); + } + else if (p_lcb->p_fixed_ccbs[xx]) + { + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason); + l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]); + p_lcb->p_fixed_ccbs[xx] = NULL; + } + } + } +} +#endif + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function l2cu_send_peer_ble_par_req +** +** Description Build and send a BLE parameter update request message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +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; + + /* Create an identifier for this packet */ + 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 ) + { + L2CAP_TRACE_WARNING0 ("l2cu_send_peer_ble_par_req - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, min_int); + UINT16_TO_STREAM (p, max_int); + UINT16_TO_STREAM (p, latency); + UINT16_TO_STREAM (p, timeout); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +/******************************************************************************* +** +** Function l2cu_send_peer_ble_par_rsp +** +** Description Build and send a BLE parameter update response message +** to the peer. +** +** Returns void +** +*******************************************************************************/ +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 ) + { + L2CAP_TRACE_WARNING0 ("l2cu_send_peer_ble_par_rsp - no buffer"); + return; + } + + p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD; + + UINT16_TO_STREAM (p, reason); + + l2c_link_check_send_pkts (p_lcb, NULL, p_buf); +} + +#endif /* BLE_INCLUDED == TRUE */ + + +/******************************************************************************* +** Functions used by both Full and Light Stack +********************************************************************************/ + +/******************************************************************************* +** +** Function l2cu_find_lcb_by_handle +** +** Description Look through all active LCBs for a match based on the +** HCI handle. +** +** Returns pointer to matched LCB, or NULL if no match +** +*******************************************************************************/ +tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle) +{ + int xx; + tL2C_LCB *p_lcb = &l2cb.lcb_pool[0]; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_lcb++) + { + if ((p_lcb->in_use) && (p_lcb->handle == handle)) + { + return (p_lcb); + } + } + + /* If here, no match found */ + return (NULL); +} + +/******************************************************************************* +** +** Function l2cu_find_ccb_by_cid +** +** Description Look through all active CCBs on a link for a match based +** on the local CID. If passed the link pointer is NULL, all +** active links are searched. +** +** Returns pointer to matched CCB, or NULL if no match +** +*******************************************************************************/ +tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid) +{ + tL2C_CCB *p_ccb = NULL; +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT8 xx; +#endif + + if (local_cid >= L2CAP_BASE_APPL_CID) + { + /* find the associated CCB by "index" */ + local_cid -= L2CAP_BASE_APPL_CID; + + if (local_cid >= MAX_L2CAP_CHANNELS) + return NULL; + + p_ccb = l2cb.ccb_pool + local_cid; + + /* make sure the CCB is in use */ + if (!p_ccb->in_use) + { + p_ccb = NULL; + } + /* make sure it's for the same LCB */ + else if (p_lcb && p_lcb != p_ccb->p_lcb) + { + p_ccb = NULL; + } + } +#if (L2CAP_UCD_INCLUDED == TRUE) + else + { + /* searching fixed channel */ + p_ccb = l2cb.ccb_pool; + for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ ) + { + if ((p_ccb->local_cid == local_cid) + &&(p_ccb->in_use) + &&(p_lcb == p_ccb->p_lcb)) + break; + else + p_ccb++; + } + if ( xx >= MAX_L2CAP_CHANNELS ) + return NULL; + } +#endif + + return (p_ccb); +} + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + +/****************************************************************************** +** +** Function l2cu_get_next_channel_in_rr +** +** Description get the next channel to send on a link. It also adjusts the +** CCB queue to do a basic priority and round-robin scheduling. +** +** Returns pointer to CCB or NULL +** +*******************************************************************************/ +static tL2C_CCB *l2cu_get_next_channel_in_rr(tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_serve_ccb = NULL; + tL2C_CCB *p_ccb; + + int i, j; + + /* scan all of priority until finding a channel to serve */ + for ( i = 0; (i < L2CAP_NUM_CHNL_PRIORITY)&&(!p_serve_ccb); i++ ) + { + /* scan all channel within serving priority group until finding a channel to serve */ + for ( j = 0; (j < p_lcb->rr_serv[p_lcb->rr_pri].num_ccb)&&(!p_serve_ccb); j++) + { + /* scaning from next serving channel */ + p_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb; + + if (!p_ccb) + { + L2CAP_TRACE_ERROR1("p_serve_ccb is NULL, rr_pri=%d", p_lcb->rr_pri); + return NULL; + } + + L2CAP_TRACE_DEBUG3("RR scan pri=%d, lcid=0x%04x, q_cout=%d", + p_ccb->ccb_priority, p_ccb->local_cid, p_ccb->xmit_hold_q.count ); + + /* store the next serving channel */ + /* this channel is the last channel of its priority group */ + if (( p_ccb->p_next_ccb == NULL ) + ||( p_ccb->p_next_ccb->ccb_priority != p_ccb->ccb_priority )) + { + /* next serving channel is set to the first channel in the group */ + p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_first_ccb; + } + else + { + /* next serving channel is set to the next channel in the group */ + p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_ccb->p_next_ccb; + } + + if (p_ccb->chnl_state != CST_OPEN) + continue; + + /* eL2CAP option in use */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) + continue; + + if ( p_ccb->fcrb.retrans_q.count == 0 ) + { + if ( p_ccb->xmit_hold_q.count == 0 ) + continue; + + /* If using the common pool, should be at least 10% free. */ + if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) ) + continue; + + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) + continue; + } + } + else + { + if (p_ccb->xmit_hold_q.count == 0) + continue; + } + + /* found a channel to serve */ + p_serve_ccb = p_ccb; + /* decrease quota of its priority group */ + p_lcb->rr_serv[p_lcb->rr_pri].quota--; + } + + /* if there is no more quota of the priority group or no channel to have data to send */ + if ((p_lcb->rr_serv[p_lcb->rr_pri].quota == 0)||(!p_serve_ccb)) + { + /* serve next priority group */ + p_lcb->rr_pri = (p_lcb->rr_pri + 1) % L2CAP_NUM_CHNL_PRIORITY; + /* initialize its quota */ + p_lcb->rr_serv[p_lcb->rr_pri].quota = L2CAP_GET_PRIORITY_QUOTA(p_lcb->rr_pri); + } + } + + if (p_serve_ccb) + { + L2CAP_TRACE_DEBUG3("RR service pri=%d, quota=%d, lcid=0x%04x", + p_serve_ccb->ccb_priority, + p_lcb->rr_serv[p_serve_ccb->ccb_priority].quota, + p_serve_ccb->local_cid ); + } + + return p_serve_ccb; +} + +#else /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + +/****************************************************************************** +** +** Function l2cu_get_next_channel +** +** Description get the next channel to send on a link bassed on priority +** scheduling. +** +** Returns pointer to CCB or NULL +** +*******************************************************************************/ +static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + + /* Get the first CCB with data to send. + */ + for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) + { + if (p_ccb->chnl_state != CST_OPEN) + continue; + + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) + continue; + + if (p_ccb->fcrb.retrans_q.count != 0) + return p_ccb; + + if (p_ccb->xmit_hold_q.count == 0) + continue; + + /* If using the common pool, should be at least 10% free. */ + if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) ) + continue; + + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) + continue; + + /* If here, we found someone */ + return p_ccb; + } + + return NULL; +} +#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */ + +/****************************************************************************** +** +** Function l2cu_get_next_buffer_to_send +** +** Description get the next buffer to send on a link. It also adjusts the +** CCB queue to do a basic priority and round-robin scheduling. +** +** Returns pointer to buffer or NULL +** +*******************************************************************************/ +BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb) +{ + tL2C_CCB *p_ccb; + BT_HDR *p_buf; + + /* Highest priority are fixed channels */ +#if (L2CAP_NUM_FIXED_CHNLS > 0) + int xx; + + for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) + { + if ((p_ccb = p_lcb->p_fixed_ccbs[xx]) == NULL) + continue; + + /* eL2CAP option in use */ + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) + continue; + + /* No more checks needed if sending from the reatransmit queue */ + if (p_ccb->fcrb.retrans_q.count == 0) + { + if (p_ccb->xmit_hold_q.count == 0) + continue; + + /* If using the common pool, should be at least 10% free. */ + if ( (p_ccb->ertm_info.fcr_tx_pool_id == HCI_ACL_POOL_ID) && (GKI_poolutilization (HCI_ACL_POOL_ID) > 90) ) + continue; + + /* If in eRTM mode, check for window closure */ + if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) + continue; + } + + if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) != NULL) + { + l2cu_set_acl_hci_header (p_buf, p_ccb); + return (p_buf); + } + } + else + { + if (p_ccb->xmit_hold_q.count != 0) + { + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q); + l2cu_set_acl_hci_header (p_buf, p_ccb); + return (p_buf); + } + } + } +#endif + +#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) + /* get next serving channel in round-robin */ + p_ccb = l2cu_get_next_channel_in_rr( p_lcb ); +#else + p_ccb = l2cu_get_next_channel( p_lcb ); +#endif + + /* Return if no buffer */ + if (p_ccb == NULL) + return (NULL); + + if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) == NULL) + return (NULL); + } + else + { + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_hold_q); + } + + if ( p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_TxComplete_Cb && (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) + (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, 1); + + + l2cu_check_channel_congestion (p_ccb); + + l2cu_set_acl_hci_header (p_buf, p_ccb); + + return (p_buf); +} + +/****************************************************************************** +** +** Function l2cu_set_acl_hci_header +** +** Description Set HCI handle for ACL packet +** +** Returns None +** +*******************************************************************************/ +void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb) +{ + UINT8 *p; + + /* Set the pointer to the beginning of the data minus 4 bytes for the packet header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset - HCI_DATA_PREAMBLE_SIZE; + +#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE) + if ( (((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_CH_BASED) && (p_ccb->is_flushable)) + || ((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_PKT) ) + { + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)); + } + else + { + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | l2cb.non_flushable_pbf); + } +#else + UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)); +#endif +#if (BLE_INCLUDED == TRUE) + if (p_ccb->p_lcb->is_ble_link) + { + /* The HCI transport will segment the buffers. */ + if (p_buf->len > btu_cb.hcit_ble_acl_data_size) + { + UINT16_TO_STREAM (p, btu_cb.hcit_ble_acl_data_size); + } + else + { + UINT16_TO_STREAM (p, p_buf->len); + } + } /* (BLE_INCLUDED == TRUE) */ + else +#endif + { + + /* The HCI transport will segment the buffers. */ + if (p_buf->len > btu_cb.hcit_acl_data_size) + { + UINT16_TO_STREAM (p, btu_cb.hcit_acl_data_size); + } + else + { + UINT16_TO_STREAM (p, p_buf->len); + } + } + p_buf->offset -= HCI_DATA_PREAMBLE_SIZE; + p_buf->len += HCI_DATA_PREAMBLE_SIZE; +} + +/****************************************************************************** +** +** Function l2cu_check_channel_congestion +** +** Description check if any change in congestion status +** +** Returns None +** +*******************************************************************************/ +void l2cu_check_channel_congestion (tL2C_CCB *p_ccb) +{ + UINT16 q_count = p_ccb->xmit_hold_q.count; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) + { + q_count += p_ccb->p_lcb->ucd_out_sec_pending_q.count; + } +#endif + + /* If the CCB queue limit is subject to a quota, check for congestion */ + + /* if this channel has outgoing traffic */ + if ((p_ccb->p_rcb)&&(p_ccb->buff_quota != 0)) + { + /* If this channel was congested */ + if ( p_ccb->cong_sent ) + { + /* If the channel is not congested now, tell the app */ + if (q_count <= (p_ccb->buff_quota / 2)) + { + p_ccb->cong_sent = FALSE; + if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) + { + L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x xmit_hold_q.count: %u buff_quota: %u", + p_ccb->local_cid, q_count, p_ccb->buff_quota); + + /* Prevent recursive calling */ + l2cb.is_cong_cback_context = TRUE; + (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE); + l2cb.is_cong_cback_context = FALSE; + } +#if (L2CAP_UCD_INCLUDED == TRUE) + else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) + { + if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) + { + L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u", + p_ccb->p_lcb->ucd_out_sec_pending_q.count, + p_ccb->xmit_hold_q.count, p_ccb->buff_quota); + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE ); + } + } +#endif + } + } + else + { + /* If this channel was not congested but it is congested now, tell the app */ + if (q_count > p_ccb->buff_quota) + { + p_ccb->cong_sent = TRUE; + if (p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) + { + L2CAP_TRACE_DEBUG3 ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u", + p_ccb->local_cid, q_count, p_ccb->buff_quota); + + (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + else if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) + { + if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) + { + L2CAP_TRACE_DEBUG3 ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u", + p_ccb->p_lcb->ucd_out_sec_pending_q.count, + p_ccb->xmit_hold_q.count, p_ccb->buff_quota); + p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE ); + } + } +#endif + } + } + } +} + diff --git a/stack/mcap/mca_api.c b/stack/mcap/mca_api.c new file mode 100644 index 0000000..1792f8c --- /dev/null +++ b/stack/mcap/mca_api.c @@ -0,0 +1,925 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 API implementation file for the Multi-Channel Adaptation + * Protocol (MCAP). + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "btm_api.h" +#include "btm_int.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" + +#include "wcassert.h" +#include "btu.h" + + +/******************************************************************************* +** +** Function mca_process_timeout +** +** Description This function is called by BTU when an MCA timer +** expires. +** +** This function is for use internal to the stack only. +** +** Returns void +** +*******************************************************************************/ +void mca_process_timeout(TIMER_LIST_ENT *p_tle) +{ + if(p_tle->event == BTU_TTYPE_MCA_CCB_RSP) + { + p_tle->event = 0; + mca_ccb_event ((tMCA_CCB *) p_tle->param, MCA_CCB_RSP_TOUT_EVT, NULL); + } +} + +/******************************************************************************* +** +** Function MCA_Init +** +** Description Initialize MCAP main control block. +** This function is called at stack start up. +** +** Returns void +** +*******************************************************************************/ +void MCA_Init(void) +{ + memset(&mca_cb, 0, sizeof(tMCA_CB)); + +#if defined(MCA_INITIAL_TRACE_LEVEL) + mca_cb.trace_level = MCA_INITIAL_TRACE_LEVEL; +#else + mca_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif +} + +/******************************************************************************* +** +** Function MCA_SetTraceLevel +** +** Description This function sets the debug trace level for MCA. +** If 0xff is passed, the current trace level is returned. +** +** Input Parameters: +** level: The level to set the MCA tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +*******************************************************************************/ +UINT8 MCA_SetTraceLevel (UINT8 level) +{ + if (level != 0xFF) + mca_cb.trace_level = level; + + return (mca_cb.trace_level); +} + +/******************************************************************************* +** +** Function MCA_Register +** +** Description This function registers an MCAP implementation. +** It is assumed that the control channel PSM and data channel +** PSM are not used by any other instances of the stack. +** If the given p_reg->ctrl_psm is 0, this handle is INT only. +** +** Returns 0, if failed. Otherwise, the MCA handle. +** +*******************************************************************************/ +tMCA_HANDLE MCA_Register(tMCA_REG *p_reg, tMCA_CTRL_CBACK *p_cback) +{ + tMCA_RCB *p_rcb; + tMCA_HANDLE handle = 0; + tL2CAP_APPL_INFO l2c_cacp_appl; + tL2CAP_APPL_INFO l2c_dacp_appl; + + WC_ASSERT(p_reg != NULL ); + WC_ASSERT(p_cback != NULL ); + + MCA_TRACE_API2 ("MCA_Register: ctrl_psm:0x%x, data_psm:0x%x", p_reg->ctrl_psm, p_reg->data_psm); + + if ( (p_rcb = mca_rcb_alloc (p_reg)) != NULL) + { + if (p_reg->ctrl_psm) + { + if (L2C_INVALID_PSM(p_reg->ctrl_psm) || L2C_INVALID_PSM(p_reg->data_psm)) + { + MCA_TRACE_ERROR0 ("INVALID_PSM"); + return 0; + } + + l2c_cacp_appl = *(tL2CAP_APPL_INFO *)&mca_l2c_int_appl; + l2c_cacp_appl.pL2CA_ConnectCfm_Cb = NULL; + l2c_dacp_appl = *(tL2CAP_APPL_INFO *)&l2c_cacp_appl; + l2c_cacp_appl.pL2CA_ConnectInd_Cb = mca_l2c_cconn_ind_cback; + l2c_dacp_appl.pL2CA_ConnectInd_Cb = mca_l2c_dconn_ind_cback; + if (L2CA_Register(p_reg->ctrl_psm, (tL2CAP_APPL_INFO *) &l2c_cacp_appl) && + L2CA_Register(p_reg->data_psm, (tL2CAP_APPL_INFO *) &l2c_dacp_appl)) + { + /* set security level */ + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_CTRL, p_reg->sec_mask, + p_reg->ctrl_psm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID); + + /* in theory, we do not need this one for data_psm + * If we don't, L2CAP rejects with security block (3), + * which is different reject code from what MCAP spec suggests. + * we set this one, so mca_l2c_dconn_ind_cback can reject /w no resources (4) */ + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_DATA, p_reg->sec_mask, + p_reg->data_psm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID); + } + else + { + MCA_TRACE_ERROR0 ("Failed to register to L2CAP"); + return 0; + } + } + else + p_rcb->reg.data_psm = 0; + handle = mca_rcb_to_handle (p_rcb); + p_rcb->p_cback = p_cback; + p_rcb->reg.rsp_tout = p_reg->rsp_tout; + } + return handle; +} + + +/******************************************************************************* +** +** Function MCA_Deregister +** +** Description This function is called to deregister an MCAP implementation. +** Before this function can be called, all control and data +** channels must be removed with MCA_DisconnectReq and MCA_CloseReq. +** +** Returns void +** +*******************************************************************************/ +void MCA_Deregister(tMCA_HANDLE handle) +{ + tMCA_RCB *p_rcb = mca_rcb_by_handle(handle); + + MCA_TRACE_API1 ("MCA_Deregister: %d", handle); + if (p_rcb && p_rcb->reg.ctrl_psm) + { + L2CA_Deregister(p_rcb->reg.ctrl_psm); + L2CA_Deregister(p_rcb->reg.data_psm); + btm_sec_clr_service_by_psm (p_rcb->reg.ctrl_psm); + btm_sec_clr_service_by_psm (p_rcb->reg.data_psm); + } + mca_rcb_dealloc(handle); +} + + +/******************************************************************************* +** +** Function MCA_CreateDep +** +** Description Create a data endpoint. If the MDEP is created successfully, +** the MDEP ID is returned in *p_dep. After a data endpoint is +** created, an application can initiate a connection between this +** endpoint and an endpoint on a peer device. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_CreateDep(tMCA_HANDLE handle, tMCA_DEP *p_dep, tMCA_CS *p_cs) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + int i; + tMCA_RCB *p_rcb = mca_rcb_by_handle(handle); + tMCA_CS *p_depcs; + + WC_ASSERT(p_dep != NULL ); + WC_ASSERT(p_cs != NULL ); + WC_ASSERT(p_cs->p_data_cback != NULL ); + + MCA_TRACE_API1 ("MCA_CreateDep: %d", handle); + if (p_rcb) + { + if (p_cs->max_mdl > MCA_NUM_MDLS) + { + MCA_TRACE_ERROR1 ("max_mdl: %d is too big", p_cs->max_mdl ); + result = MCA_BAD_PARAMS; + } + else + { + p_depcs = p_rcb->dep; + if (p_cs->type == MCA_TDEP_ECHO) + { + if (p_depcs->p_data_cback) + { + MCA_TRACE_ERROR0 ("Already has ECHO MDEP"); + return MCA_NO_RESOURCES; + } + memcpy (p_depcs, p_cs, sizeof (tMCA_CS)); + *p_dep = 0; + result = MCA_SUCCESS; + } + else + { + result = MCA_NO_RESOURCES; + /* non-echo MDEP starts from 1 */ + p_depcs++; + for (i=1; ip_data_cback == NULL) + { + memcpy (p_depcs, p_cs, sizeof (tMCA_CS)); + /* internally use type as the mdep id */ + p_depcs->type = i; + *p_dep = i; + result = MCA_SUCCESS; + break; + } + } + } + } + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_DeleteDep +** +** Description Delete a data endpoint. This function is called when +** the implementation is no longer using a data endpoint. +** If this function is called when the endpoint is connected +** the connection is closed and the data endpoint +** is removed. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_DeleteDep(tMCA_HANDLE handle, tMCA_DEP dep) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_RCB *p_rcb = mca_rcb_by_handle(handle); + tMCA_DCB *p_dcb; + int i, max; + tMCA_CS *p_depcs; + + MCA_TRACE_API2 ("MCA_DeleteDep: %d dep:%d", handle, dep); + if (p_rcb) + { + if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback) + { + result = MCA_SUCCESS; + p_rcb->dep[dep].p_data_cback = NULL; + p_depcs = &(p_rcb->dep[dep]); + i = handle - 1; + max = MCA_NUM_MDLS*MCA_NUM_LINKS; + p_dcb = &mca_cb.dcb[i*max]; + /* make sure no MDL exists for this MDEP */ + for (i=0; istate && p_dcb->p_cs == p_depcs) + { + mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); + } + } + } + } + return result; +} + +/******************************************************************************* +** +** Function MCA_ConnectReq +** +** Description This function initiates an MCAP control channel connection +** to the peer device. When the connection is completed, an +** MCA_CONNECT_IND_EVT is reported to the application via its +** control callback function. +** This control channel is identified by the tMCA_CL. +** If the connection attempt fails, an MCA_DISCONNECT_IND_EVT is +** reported. The security mask parameter overrides the outgoing +** security mask set in MCA_Register(). +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_ConnectReq(tMCA_HANDLE handle, BD_ADDR bd_addr, + UINT16 ctrl_psm, UINT16 sec_mask) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb; + tMCA_TC_TBL *p_tbl; + + MCA_TRACE_API2 ("MCA_ConnectReq: %d psm:0x%x", handle, ctrl_psm); + if ((p_ccb = mca_ccb_by_bd(handle, bd_addr)) == NULL) + p_ccb = mca_ccb_alloc(handle, bd_addr); + else + { + MCA_TRACE_ERROR0 ("control channel already exists"); + return MCA_BUSY; + } + + if (p_ccb) + { + p_ccb->ctrl_vpsm = L2CA_Register (ctrl_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl); + result = MCA_NO_RESOURCES; + if (p_ccb->ctrl_vpsm) + { + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_CTRL, sec_mask, + p_ccb->ctrl_vpsm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID); + p_ccb->lcid = mca_l2c_open_req(bd_addr, p_ccb->ctrl_vpsm, NULL); + if (p_ccb->lcid) + { + p_tbl = mca_tc_tbl_calloc(p_ccb); + if (p_tbl) + { + p_tbl->state = MCA_TC_ST_CONN; + p_ccb->sec_mask = sec_mask; + result = MCA_SUCCESS; + } + } + } + if (result != MCA_SUCCESS) + mca_ccb_dealloc (p_ccb, NULL); + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_DisconnectReq +** +** Description This function disconnect an MCAP control channel +** to the peer device. +** If associated data channel exists, they are disconnected. +** When the MCL is disconnected an MCA_DISCONNECT_IND_EVT is +** reported to the application via its control callback function. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_DisconnectReq(tMCA_CL mcl) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + + MCA_TRACE_API1 ("MCA_DisconnectReq: %d ", mcl); + if (p_ccb) + { + result = MCA_SUCCESS; + mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL); + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_CreateMdl +** +** Description This function sends a CREATE_MDL request to the peer device. +** When the response is received, a MCA_CREATE_CFM_EVT is reported +** with the given MDL ID. +** If the response is successful, a data channel is open +** with the given p_chnl_cfg +** If p_chnl_cfg is NULL, the data channel is not initiated until +** MCA_DataChnlCfg is called to provide the p_chnl_cfg. +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_CreateMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm, + UINT16 mdl_id, UINT8 peer_dep_id, + UINT8 cfg, const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG *p_evt_data; + tMCA_DCB *p_dcb; + + MCA_TRACE_API4 ("MCA_CreateMdl: %d dep=%d mdl_id=%d peer_dep_id=%d", mcl, dep, mdl_id, peer_dep_id); + if (p_ccb) + { + if (p_ccb->p_tx_req || p_ccb->p_rx_msg || p_ccb->cong) + { + MCA_TRACE_ERROR0 ("pending req"); + return MCA_BUSY; + } + + if ((peer_dep_id > MCA_MAX_MDEP_ID) || (!MCA_IS_VALID_MDL_ID(mdl_id))) + { + MCA_TRACE_ERROR2 ("bad peer dep id:%d or bad mdl id: %d ", peer_dep_id, mdl_id); + return MCA_BAD_PARAMS; + } + + if (mca_ccb_uses_mdl_id(p_ccb, mdl_id)) + { + MCA_TRACE_ERROR1 ("mdl id: %d is used in the control link", mdl_id); + return MCA_BAD_MDL_ID; + } + + p_dcb = mca_dcb_alloc(p_ccb, dep); + result = MCA_NO_RESOURCES; + if (p_dcb) + { + /* save the info required by dcb connection */ + p_dcb->p_chnl_cfg = p_chnl_cfg; + p_dcb->mdl_id = mdl_id; + p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG)); + if (p_evt_data) + { + if (!p_ccb->data_vpsm) + p_ccb->data_vpsm = L2CA_Register (data_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl); + if (p_ccb->data_vpsm) + { + p_evt_data->dcb_idx = mca_dcb_to_hdl (p_dcb); + p_evt_data->mdep_id = peer_dep_id; + p_evt_data->mdl_id = mdl_id; + p_evt_data->param = cfg; + p_evt_data->op_code = MCA_OP_MDL_CREATE_REQ; + p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT; + p_evt_data->hdr.layer_specific = FALSE; + mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data); + return MCA_SUCCESS; + } + else + GKI_freebuf (p_evt_data); + } + mca_dcb_dealloc(p_dcb, NULL); + } + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_CreateMdlRsp +** +** Description This function sends a CREATE_MDL response to the peer device +** in response to a received MCA_CREATE_IND_EVT. +** If the rsp_code is successful, a data channel is open +** with the given p_chnl_cfg +** When the data channel is open successfully, a MCA_OPEN_IND_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_CreateMdlRsp(tMCA_CL mcl, tMCA_DEP dep, + UINT16 mdl_id, UINT8 cfg, UINT8 rsp_code, + const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG evt_data; + tMCA_DCB *p_dcb; + + MCA_TRACE_API5 ("MCA_CreateMdlRsp: %d dep=%d mdl_id=%d cfg=%d rsp_code=%d", mcl, dep, mdl_id, cfg, rsp_code); + WC_ASSERT(p_chnl_cfg != NULL ); + if (p_ccb) + { + if (p_ccb->cong) + { + MCA_TRACE_ERROR0 ("congested"); + return MCA_BUSY; + } + if (p_ccb->p_rx_msg && (p_ccb->p_rx_msg->mdep_id == dep ) + && (p_ccb->p_rx_msg->mdl_id == mdl_id) && (p_ccb->p_rx_msg->op_code == MCA_OP_MDL_CREATE_REQ)) + { + result = MCA_SUCCESS; + evt_data.dcb_idx = 0; + if (rsp_code == MCA_RSP_SUCCESS) + { + p_dcb = mca_dcb_alloc(p_ccb, dep); + if (p_dcb) + { + evt_data.dcb_idx = mca_dcb_to_hdl(p_dcb); + p_dcb->p_chnl_cfg = p_chnl_cfg; + p_dcb->mdl_id = mdl_id; + } + else + { + rsp_code = MCA_RSP_MDEP_BUSY; + result = MCA_NO_RESOURCES; + } + } + + if (result == MCA_SUCCESS) + { + evt_data.mdl_id = mdl_id; + evt_data.param = cfg; + evt_data.rsp_code = rsp_code; + evt_data.op_code = MCA_OP_MDL_CREATE_RSP; + mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, (tMCA_CCB_EVT *)&evt_data); + } + } + else + { + MCA_TRACE_ERROR0 ("The given MCL is not expecting a MCA_CreateMdlRsp with the given parameters" ); + result = MCA_BAD_PARAMS; + } + } + return result; +} + +/******************************************************************************* +** +** Function MCA_CloseReq +** +** Description Close a data channel. When the channel is closed, an +** MCA_CLOSE_CFM_EVT is sent to the application via the +** control callback function for this handle. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_CloseReq(tMCA_DL mdl) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl); + + MCA_TRACE_API1 ("MCA_CloseReq: %d ", mdl); + if (p_dcb) + { + result = MCA_SUCCESS; + mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_ReconnectMdl +** +** Description This function sends a RECONNECT_MDL request to the peer device. +** When the response is received, a MCA_RECONNECT_CFM_EVT is reported. +** If p_chnl_cfg is NULL, the data channel is not initiated until +** MCA_DataChnlCfg is called to provide the p_chnl_cfg. +** If the response is successful, a data channel is open. +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_ReconnectMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm, + UINT16 mdl_id, const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG *p_evt_data; + tMCA_DCB *p_dcb; + + MCA_TRACE_API1 ("MCA_ReconnectMdl: %d ", mcl); + WC_ASSERT(p_chnl_cfg != NULL ); + if (p_ccb) + { + if (p_ccb->p_tx_req || p_ccb->p_rx_msg || p_ccb->cong) + { + MCA_TRACE_ERROR0 ("pending req"); + return MCA_BUSY; + } + + if (!MCA_IS_VALID_MDL_ID(mdl_id)) + { + MCA_TRACE_ERROR1 ("bad mdl id: %d ", mdl_id); + return MCA_BAD_PARAMS; + } + + if (mca_ccb_uses_mdl_id(p_ccb, mdl_id)) + { + MCA_TRACE_ERROR1 ("mdl id: %d is used in the control link", mdl_id); + return MCA_BAD_MDL_ID; + } + + p_dcb = mca_dcb_alloc(p_ccb, dep); + result = MCA_NO_RESOURCES; + if (p_dcb) + { + p_dcb->p_chnl_cfg = p_chnl_cfg; + p_dcb->mdl_id = mdl_id; + p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG)); + if (p_evt_data) + { + if (!p_ccb->data_vpsm) + p_ccb->data_vpsm = L2CA_Register (data_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl); + p_evt_data->dcb_idx = mca_dcb_to_hdl(p_dcb); + p_evt_data->mdl_id = mdl_id; + p_evt_data->op_code = MCA_OP_MDL_RECONNECT_REQ; + p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT; + mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data); + return MCA_SUCCESS; + } + mca_dcb_dealloc(p_dcb, NULL); + } + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_ReconnectMdlRsp +** +** Description This function sends a RECONNECT_MDL response to the peer device +** in response to a MCA_RECONNECT_IND_EVT event. +** If the response is successful, a data channel is open. +** When the data channel is open successfully, a MCA_OPEN_IND_EVT +** is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_ReconnectMdlRsp(tMCA_CL mcl, tMCA_DEP dep, + UINT16 mdl_id, UINT8 rsp_code, + const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG evt_data; + tMCA_DCB *p_dcb; + + MCA_TRACE_API1 ("MCA_ReconnectMdlRsp: %d ", mcl); + WC_ASSERT(p_chnl_cfg != NULL ); + if (p_ccb) + { + if (p_ccb->cong) + { + MCA_TRACE_ERROR0 ("congested"); + return MCA_BUSY; + } + if (p_ccb->p_rx_msg && (p_ccb->p_rx_msg->mdl_id == mdl_id) && + (p_ccb->p_rx_msg->op_code == MCA_OP_MDL_RECONNECT_REQ)) + { + result = MCA_SUCCESS; + evt_data.dcb_idx = 0; + if (rsp_code == MCA_RSP_SUCCESS) + { + p_dcb = mca_dcb_alloc(p_ccb, dep); + if (p_dcb) + { + evt_data.dcb_idx = mca_dcb_to_hdl(p_dcb); + p_dcb->p_chnl_cfg = p_chnl_cfg; + p_dcb->mdl_id = mdl_id; + } + else + { + MCA_TRACE_ERROR0 ("Out of MDL for this MDEP"); + rsp_code = MCA_RSP_MDEP_BUSY; + result = MCA_NO_RESOURCES; + } + } + + evt_data.mdl_id = mdl_id; + evt_data.rsp_code = rsp_code; + evt_data.op_code = MCA_OP_MDL_RECONNECT_RSP; + mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, (tMCA_CCB_EVT *)&evt_data); + } + else + { + MCA_TRACE_ERROR0 ("The given MCL is not expecting a MCA_ReconnectMdlRsp with the given parameters" ); + result = MCA_BAD_PARAMS; + } + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_DataChnlCfg +** +** Description This function initiates a data channel connection toward the +** connected peer device. +** When the data channel is open successfully, a MCA_OPEN_CFM_EVT +** is reported. This data channel is identified as tMCA_DL. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_DataChnlCfg(tMCA_CL mcl, const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_DCB *p_dcb; + tMCA_TC_TBL *p_tbl; + + MCA_TRACE_API1 ("MCA_DataChnlCfg: %d ", mcl); + WC_ASSERT(p_chnl_cfg != NULL ); + if (p_ccb) + { + result = MCA_NO_RESOURCES; + if ((p_ccb->p_tx_req == NULL) || (p_ccb->status != MCA_CCB_STAT_PENDING) || + ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) == NULL)) + { + MCA_TRACE_ERROR1 ("The given MCL is not expecting this API:%d", p_ccb->status); + return result; + } + + p_dcb->p_chnl_cfg = p_chnl_cfg; + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask, + p_ccb->data_vpsm, BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx); + p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, p_dcb->p_chnl_cfg); + if (p_dcb->lcid) + { + p_tbl = mca_tc_tbl_dalloc(p_dcb); + if (p_tbl) + { + p_tbl->state = MCA_TC_ST_CONN; + result = MCA_SUCCESS; + } + } + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_Abort +** +** Description This function sends a ABORT_MDL request to the peer device. +** When the response is received, a MCA_ABORT_CFM_EVT is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_Abort(tMCA_CL mcl) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG *p_evt_data; + tMCA_DCB *p_dcb; + + MCA_TRACE_API1 ("MCA_Abort: %d", mcl); + if (p_ccb) + { + result = MCA_NO_RESOURCES; + /* verify that we are waiting for data channel to come up with the given mdl */ + if ((p_ccb->p_tx_req == NULL) || (p_ccb->status != MCA_CCB_STAT_PENDING) || + ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) == NULL)) + { + MCA_TRACE_ERROR1 ("The given MCL is not expecting this API:%d", p_ccb->status); + return result; + } + + if (p_ccb->cong) + { + MCA_TRACE_ERROR0 ("congested"); + return MCA_BUSY; + } + + result = MCA_NO_RESOURCES; + p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG)); + if (p_evt_data) + { + result = MCA_SUCCESS; + p_evt_data->op_code = MCA_OP_MDL_ABORT_REQ; + p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT; + mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data); + } + + } + return result; +} + + +/******************************************************************************* +** +** Function MCA_Delete +** +** Description This function sends a DELETE_MDL request to the peer device. +** When the response is received, a MCA_DELETE_CFM_EVT is reported. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_Delete(tMCA_CL mcl, UINT16 mdl_id) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl); + tMCA_CCB_MSG *p_evt_data; + + MCA_TRACE_API1 ("MCA_Delete: %d ", mcl); + if (p_ccb) + { + if (p_ccb->cong) + { + MCA_TRACE_ERROR0 ("congested"); + return MCA_BUSY; + } + if (!MCA_IS_VALID_MDL_ID(mdl_id) && (mdl_id != MCA_ALL_MDL_ID)) + { + MCA_TRACE_ERROR1 ("bad mdl id: %d ", mdl_id); + return MCA_BAD_PARAMS; + } + p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG)); + if (p_evt_data) + { + result = MCA_SUCCESS; + p_evt_data->mdl_id = mdl_id; + p_evt_data->op_code = MCA_OP_MDL_DELETE_REQ; + p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT; + mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data); + } + else + result = MCA_NO_RESOURCES; + } + return result; +} + +/******************************************************************************* +** +** Function MCA_WriteReq +** +** Description Send a data packet to the peer device. +** +** The application passes the packet using the BT_HDR structure. +** The offset field must be equal to or greater than L2CAP_MIN_OFFSET. +** This allows enough space in the buffer for the L2CAP header. +** +** The memory pointed to by p_pkt must be a GKI buffer +** allocated by the application. This buffer will be freed +** by the protocol stack; the application must not free +** this buffer. +** +** Returns MCA_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +tMCA_RESULT MCA_WriteReq(tMCA_DL mdl, BT_HDR *p_pkt) +{ + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl); + tMCA_DCB_EVT evt_data; + + MCA_TRACE_API1 ("MCA_WriteReq: %d ", mdl); + if (p_dcb) + { + if (p_dcb->cong) + { + result = MCA_BUSY; + } + else + { + evt_data.p_pkt = p_pkt; + result = MCA_SUCCESS; + mca_dcb_event(p_dcb, MCA_DCB_API_WRITE_EVT, &evt_data); + } + } + return result; +} + +/******************************************************************************* +** +** Function MCA_GetL2CapChannel +** +** Description Get the L2CAP CID used by the given data channel handle. +** +** Returns L2CAP channel ID if successful, otherwise 0. +** +*******************************************************************************/ +UINT16 MCA_GetL2CapChannel (tMCA_DL mdl) +{ + UINT16 lcid = 0; + tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl); + + MCA_TRACE_API1 ("MCA_GetL2CapChannel: %d ", mdl); + if (p_dcb) + lcid = p_dcb->lcid; + return lcid; +} + diff --git a/stack/mcap/mca_cact.c b/stack/mcap/mca_cact.c new file mode 100644 index 0000000..85f19f4 --- /dev/null +++ b/stack/mcap/mca_cact.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP Control Channel Action + * Functions. + * + ******************************************************************************/ +#include +#include "bt_target.h" +#include "gki.h" +#include "btm_api.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" + + +#include "btu.h" +/***************************************************************************** +** constants +*****************************************************************************/ +/******************************************************************************* +** +** Function mca_ccb_rsp_tout +** +** Description This function processes the response timeout. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_rsp_tout(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + tMCA_CTRL evt_data; + mca_ccb_report_event(p_ccb, MCA_RSP_TOUT_IND_EVT, &evt_data); +} + +/******************************************************************************* +** +** Function mca_ccb_report_event +** +** Description This function reports the given event. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_report_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CTRL *p_data) +{ + if (p_ccb && p_ccb->p_rcb && p_ccb->p_rcb->p_cback) + (*p_ccb->p_rcb->p_cback)(mca_rcb_to_handle(p_ccb->p_rcb), mca_ccb_to_hdl(p_ccb), event, p_data); +} + +/******************************************************************************* +** +** Function mca_ccb_free_msg +** +** Description This function frees the received message. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_free_msg(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + GKI_freebuf (p_data); +} + +/******************************************************************************* +** +** Function mca_ccb_snd_req +** +** Description This function builds a request and sends it to the peer. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_snd_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data; + BT_HDR *p_pkt; + UINT8 *p, *p_start; + BOOLEAN is_abort = FALSE; + tMCA_DCB *p_dcb; + + MCA_TRACE_DEBUG2 ("mca_ccb_snd_req cong=%d req=%d", p_ccb->cong, p_msg->op_code); + /* check for abort request */ + if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (p_msg->op_code == MCA_OP_MDL_ABORT_REQ)) + { + p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx); + /* the Abort API does not have the associated mdl_id. + * Get the mdl_id in dcb to compose the request */ + p_msg->mdl_id = p_dcb->mdl_id; + mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); + mca_free_buf ((void **)&p_ccb->p_tx_req); + p_ccb->status = MCA_CCB_STAT_NORM; + is_abort = TRUE; + } + + /* no pending outgoing messages or it's an abort request for a pending data channel */ + if ((!p_ccb->p_tx_req) || is_abort) + { + p_ccb->p_tx_req = p_msg; + if (!p_ccb->cong) + { + p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU); + if (p_pkt) + { + p_pkt->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET; + *p++ = p_msg->op_code; + UINT16_TO_BE_STREAM (p, p_msg->mdl_id); + if (p_msg->op_code == MCA_OP_MDL_CREATE_REQ) + { + *p++ = p_msg->mdep_id; + *p++ = p_msg->param; + } + p_msg->hdr.layer_specific = TRUE; /* mark this message as sent */ + p_pkt->len = p - p_start; + L2CA_DataWrite (p_ccb->lcid, p_pkt); + p_ccb->timer_entry.param = (TIMER_PARAM_TYPE) p_ccb; + btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_MCA_CCB_RSP, p_ccb->p_rcb->reg.rsp_tout); + } + } + /* else the L2CAP channel is congested. keep the message to be sent later */ + } + else + { + MCA_TRACE_WARNING0 ("dropping api req"); + GKI_freebuf (p_data); + } +} + +/******************************************************************************* +** +** Function mca_ccb_snd_rsp +** +** Description This function builds a response and sends it to +** the peer. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_snd_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data; + BT_HDR *p_pkt; + UINT8 *p, *p_start; + BOOLEAN chk_mdl = FALSE; + tMCA_DCB *p_dcb; + + MCA_TRACE_DEBUG2 ("mca_ccb_snd_rsp cong=%d req=%d", p_ccb->cong, p_msg->op_code); + /* assume that API functions verified the parameters */ + p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU); + if (p_pkt) + { + p_pkt->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET; + *p++ = p_msg->op_code; + *p++ = p_msg->rsp_code; + UINT16_TO_BE_STREAM (p, p_msg->mdl_id); + if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP) + { + *p++ = p_msg->param; + chk_mdl = TRUE; + } + else if (p_msg->op_code == MCA_OP_MDL_RECONNECT_RSP) + chk_mdl = TRUE; + + if (chk_mdl && p_msg->rsp_code == MCA_RSP_SUCCESS) + { + p_dcb = mca_dcb_by_hdl(p_msg->dcb_idx); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask, + p_ccb->p_rcb->reg.data_psm, BTM_SEC_PROTO_MCA, p_msg->dcb_idx); + p_ccb->status = MCA_CCB_STAT_PENDING; + /* set p_tx_req to block API_REQ/API_RSP before DL is up */ + mca_free_buf ((void **)&p_ccb->p_tx_req); + p_ccb->p_tx_req = p_ccb->p_rx_msg; + p_ccb->p_rx_msg = NULL; + p_ccb->p_tx_req->dcb_idx = p_msg->dcb_idx; + } + mca_free_buf ((void **)&p_ccb->p_rx_msg); + p_pkt->len = p - p_start; + L2CA_DataWrite (p_ccb->lcid, p_pkt); + } + +} + +/******************************************************************************* +** +** Function mca_ccb_do_disconn +** +** Description This function closes a control channel. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_do_disconn (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + mca_dcb_close_by_mdl_id (p_ccb, MCA_ALL_MDL_ID); + L2CA_DisconnectReq(p_ccb->lcid); +} + +/******************************************************************************* +** +** Function mca_ccb_cong +** +** Description This function sets the congestion state for the CCB. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_cong(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + MCA_TRACE_DEBUG2 ("mca_ccb_cong cong=%d/%d", p_ccb->cong, p_data->llcong); + p_ccb->cong = p_data->llcong; + if (!p_ccb->cong) + { + /* if there's a held packet, send it now */ + if (p_ccb->p_tx_req && !p_ccb->p_tx_req->hdr.layer_specific) + { + p_data = (tMCA_CCB_EVT *)p_ccb->p_tx_req; + p_ccb->p_tx_req = NULL; + mca_ccb_snd_req (p_ccb, p_data); + } + } +} + +/******************************************************************************* +** +** Function mca_ccb_hdl_req +** +** Description This function is called when a MCAP request is received from +** the peer. It calls the application callback function to +** report the event. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_hdl_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + BT_HDR *p_pkt = &p_data->hdr; + BT_HDR *p_buf; + UINT8 *p, *p_start; + tMCA_DCB *p_dcb; + tMCA_CTRL evt_data; + tMCA_CCB_MSG *p_rx_msg = NULL; + UINT8 reject_code = MCA_RSP_NO_RESOURCE; + BOOLEAN send_rsp = FALSE; + BOOLEAN check_req = FALSE; + UINT8 reject_opcode; + + MCA_TRACE_DEBUG1 ("mca_ccb_hdl_req status:%d", p_ccb->status); + p_rx_msg = (tMCA_CCB_MSG *)p_pkt; + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + evt_data.hdr.op_code = *p++; + BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p); + reject_opcode = evt_data.hdr.op_code+1; + + MCA_TRACE_DEBUG1 ("received mdl id: %d ", evt_data.hdr.mdl_id); + if (p_ccb->status == MCA_CCB_STAT_PENDING) + { + MCA_TRACE_DEBUG0 ("received req inpending state"); + /* allow abort in pending state */ + if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (evt_data.hdr.op_code == MCA_OP_MDL_ABORT_REQ)) + { + reject_code = MCA_RSP_SUCCESS; + send_rsp = TRUE; + /* clear the pending status */ + p_ccb->status = MCA_CCB_STAT_NORM; + if (p_ccb->p_tx_req && ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx))!= NULL)) + { + mca_dcb_dealloc (p_dcb, NULL); + mca_free_buf ((void **)&p_ccb->p_tx_req); + } + } + else + reject_code = MCA_RSP_BAD_OP; + } + else if (p_ccb->p_rx_msg) + { + MCA_TRACE_DEBUG0 ("still handling prev req"); + /* still holding previous message, reject this new one ?? */ + + } + else if (p_ccb->p_tx_req) + { + MCA_TRACE_DEBUG1 ("still waiting for a response ctrl_vpsm:0x%x", p_ccb->ctrl_vpsm); + /* sent a request; waiting for response */ + if (p_ccb->ctrl_vpsm == 0) + { + MCA_TRACE_DEBUG0 ("local is ACP. accept the cmd from INT"); + /* local is acceptor, need to handle the request */ + check_req = TRUE; + reject_code = MCA_RSP_SUCCESS; + /* drop the previous request */ + if ((p_ccb->p_tx_req->op_code == MCA_OP_MDL_CREATE_REQ) && + ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) + { + mca_dcb_dealloc(p_dcb, NULL); + } + mca_free_buf ((void **)&p_ccb->p_tx_req); + mca_stop_timer(p_ccb); + } + else + { + /* local is initiator, ignore the req */ + GKI_freebuf (p_pkt); + return; + } + } + else if (p_pkt->layer_specific != MCA_RSP_SUCCESS) + { + + reject_code = (UINT8)p_pkt->layer_specific; + if (((evt_data.hdr.op_code >= MCA_NUM_STANDARD_OPCODE) && + (evt_data.hdr.op_code < MCA_FIRST_SYNC_OP)) || + (evt_data.hdr.op_code > MCA_LAST_SYNC_OP)) + { + /* invalid op code */ + reject_opcode = MCA_OP_ERROR_RSP; + evt_data.hdr.mdl_id = 0; + } + } + else + { + check_req = TRUE; + reject_code = MCA_RSP_SUCCESS; + } + + if (check_req) + { + if (reject_code == MCA_RSP_SUCCESS) + { + reject_code = MCA_RSP_BAD_MDL; + if (MCA_IS_VALID_MDL_ID(evt_data.hdr.mdl_id) || + ((evt_data.hdr.mdl_id == MCA_ALL_MDL_ID) && (evt_data.hdr.op_code == MCA_OP_MDL_DELETE_REQ))) + { + reject_code = MCA_RSP_SUCCESS; + /* mdl_id is valid according to the spec */ + switch (evt_data.hdr.op_code) + { + case MCA_OP_MDL_CREATE_REQ: + evt_data.create_ind.dep_id = *p++; + evt_data.create_ind.cfg = *p++; + p_rx_msg->mdep_id = evt_data.create_ind.dep_id; + if (!mca_is_valid_dep_id(p_ccb->p_rcb, p_rx_msg->mdep_id)) + { + MCA_TRACE_ERROR0 ("not a valid local mdep id"); + reject_code = MCA_RSP_BAD_MDEP; + } + else if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) + { + MCA_TRACE_DEBUG0 ("the mdl_id is currently used in the CL(create)"); + mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id); + } + else + { + /* check if this dep still have MDL available */ + if (mca_dep_free_mdl(p_ccb, evt_data.create_ind.dep_id) == 0) + { + MCA_TRACE_ERROR0 ("the mdep is currently using max_mdl"); + reject_code = MCA_RSP_MDEP_BUSY; + } + } + break; + + case MCA_OP_MDL_RECONNECT_REQ: + if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id)) + { + MCA_TRACE_ERROR0 ("the mdl_id is currently used in the CL(reconn)"); + reject_code = MCA_RSP_MDL_BUSY; + } + break; + + case MCA_OP_MDL_ABORT_REQ: + reject_code = MCA_RSP_BAD_OP; + break; + + case MCA_OP_MDL_DELETE_REQ: + /* delete the associated mdl */ + mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id); + send_rsp = TRUE; + break; + } + } + } + } + + if (((reject_code != MCA_RSP_SUCCESS) && (evt_data.hdr.op_code != MCA_OP_SYNC_INFO_IND)) + || send_rsp) + { + p_buf = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU); + if (p_buf) + { + p_buf->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8*)(p_buf + 1) + L2CAP_MIN_OFFSET; + *p++ = reject_opcode; + *p++ = reject_code; + UINT16_TO_BE_STREAM (p, evt_data.hdr.mdl_id); + /* + if (((*p_start) == MCA_OP_MDL_CREATE_RSP) && (reject_code == MCA_RSP_SUCCESS)) + { + *p++ = evt_data.create_ind.cfg; + } + */ + + p_buf->len = p - p_start; + L2CA_DataWrite (p_ccb->lcid, p_buf); + } + } + + if (reject_code == MCA_RSP_SUCCESS) + { + /* use the received GKI buffer to store information to double check response API */ + p_rx_msg->op_code = evt_data.hdr.op_code; + p_rx_msg->mdl_id = evt_data.hdr.mdl_id; + p_ccb->p_rx_msg = p_rx_msg; + if (send_rsp) + { + GKI_freebuf (p_pkt); + p_ccb->p_rx_msg = NULL; + } + mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data); + } + else + GKI_freebuf (p_pkt); +} + +/******************************************************************************* +** +** Function mca_ccb_hdl_rsp +** +** Description This function is called when a MCAP response is received from +** the peer. It calls the application callback function with +** the results. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_hdl_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + BT_HDR *p_pkt = &p_data->hdr; + UINT8 *p; + tMCA_CTRL evt_data; + BOOLEAN chk_mdl = FALSE; + tMCA_DCB *p_dcb; + tMCA_RESULT result = MCA_BAD_HANDLE; + tMCA_TC_TBL *p_tbl; + + if (p_ccb->p_tx_req) + { + /* verify that the received response matches the sent request */ + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + evt_data.hdr.op_code = *p++; + if ((evt_data.hdr.op_code == 0) || + ((p_ccb->p_tx_req->op_code + 1) == evt_data.hdr.op_code)) + { + evt_data.rsp.rsp_code = *p++; + mca_stop_timer(p_ccb); + BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p); + if (evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP) + { + evt_data.create_cfm.cfg = *p++; + chk_mdl = TRUE; + } + else if (evt_data.hdr.op_code == MCA_OP_MDL_RECONNECT_RSP) + chk_mdl = TRUE; + + if (chk_mdl) + { + p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx); + if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) + { + if (evt_data.hdr.mdl_id != p_dcb->mdl_id) + { + MCA_TRACE_ERROR2 ("peer's mdl_id=%d != our mdl_id=%d", evt_data.hdr.mdl_id, p_dcb->mdl_id); + /* change the response code to be an error */ + if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS) + { + evt_data.rsp.rsp_code = MCA_RSP_BAD_MDL; + /* send Abort */ + p_ccb->status = MCA_CCB_STAT_PENDING; + MCA_Abort(mca_ccb_to_hdl(p_ccb)); + } + } + else if (p_dcb->p_chnl_cfg) + { + /* the data channel configuration is known. Proceed with data channel initiation */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask, + p_ccb->data_vpsm, BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx); + p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, p_dcb->p_chnl_cfg); + if (p_dcb->lcid) + { + p_tbl = mca_tc_tbl_dalloc(p_dcb); + if (p_tbl) + { + p_tbl->state = MCA_TC_ST_CONN; + p_ccb->status = MCA_CCB_STAT_PENDING; + result = MCA_SUCCESS; + } + } + } + else + { + /* mark this MCL as pending and wait for MCA_DataChnlCfg */ + p_ccb->status = MCA_CCB_STAT_PENDING; + result = MCA_SUCCESS; + } + } + + if (result != MCA_SUCCESS && p_dcb) + { + mca_dcb_dealloc(p_dcb, NULL); + } + } /* end of chk_mdl */ + + if (p_ccb->status != MCA_CCB_STAT_PENDING) + mca_free_buf ((void **)&p_ccb->p_tx_req); + mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data); + } + /* else a bad response is received */ + } + else + { + /* not expecting any response. drop it */ + MCA_TRACE_WARNING0 ("dropping received rsp (not expecting a response)"); + } + GKI_freebuf (p_data); +} + +/******************************************************************************* +** +** Function mca_ccb_ll_open +** +** Description This function is called to report MCA_CONNECT_IND_EVT event. +** It also clears the congestion flag (ccb.cong). +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_ll_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + tMCA_CTRL evt_data; + p_ccb->cong = FALSE; + evt_data.connect_ind.mtu = p_data->open.peer_mtu; + memcpy (evt_data.connect_ind.bd_addr, p_ccb->peer_addr, BD_ADDR_LEN); + mca_ccb_report_event (p_ccb, MCA_CONNECT_IND_EVT, &evt_data); +} + +/******************************************************************************* +** +** Function mca_ccb_dl_open +** +** Description This function is called when data channel is open. +** It clears p_tx_req to allow other message exchage on this CL. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_dl_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + mca_free_buf ((void **)&p_ccb->p_tx_req); + mca_free_buf ((void **)&p_ccb->p_rx_msg); + p_ccb->status = MCA_CCB_STAT_NORM; +} + diff --git a/stack/mcap/mca_csm.c b/stack/mcap/mca_csm.c new file mode 100644 index 0000000..af43bef --- /dev/null +++ b/stack/mcap/mca_csm.c @@ -0,0 +1,385 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP Control channel state + * machine. + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" +#include "btu.h" + +/***************************************************************************** +** data channel state machine constants and types +*****************************************************************************/ +enum +{ + MCA_CCB_FREE_MSG, + MCA_CCB_SND_REQ, + MCA_CCB_SND_RSP, + MCA_CCB_DO_DISCONN, + MCA_CCB_CONG, + MCA_CCB_HDL_REQ, + MCA_CCB_HDL_RSP, + MCA_CCB_LL_OPEN, + MCA_CCB_DL_OPEN, + MCA_CCB_DEALLOC, + MCA_CCB_RSP_TOUT, + MCA_CCB_NUM_ACTIONS +}; +#define MCA_CCB_IGNORE MCA_CCB_NUM_ACTIONS + +/* action function list */ +const tMCA_CCB_ACTION mca_ccb_action[] = { + mca_ccb_free_msg, + mca_ccb_snd_req, + mca_ccb_snd_rsp, + mca_ccb_do_disconn, + mca_ccb_cong, + mca_ccb_hdl_req, + mca_ccb_hdl_rsp, + mca_ccb_ll_open, + mca_ccb_dl_open, + mca_ccb_dealloc, + mca_ccb_rsp_tout, +}; + +/* state table information */ +#define MCA_CCB_ACTIONS 1 /* number of actions */ +#define MCA_CCB_ACT_COL 0 /* position of action function */ +#define MCA_CCB_NEXT_STATE 1 /* position of next state */ +#define MCA_CCB_NUM_COLS 2 /* number of columns in state tables */ + +/* state table for opening state */ +const UINT8 mca_ccb_st_opening[][MCA_CCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST}, +/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_DO_DISCONN, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST}, +/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST}, +/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST}, +/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST}, +/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST}, +/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_LL_OPEN, MCA_CCB_OPEN_ST}, +/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST}, +/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_CONG, MCA_CCB_OPENING_ST}, +/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 mca_ccb_st_open[][MCA_CCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPEN_ST}, +/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_DO_DISCONN, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_SND_REQ, MCA_CCB_OPEN_ST}, +/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_SND_RSP, MCA_CCB_OPEN_ST}, +/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_HDL_REQ, MCA_CCB_OPEN_ST}, +/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_HDL_RSP, MCA_CCB_OPEN_ST}, +/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_DL_OPEN, MCA_CCB_OPEN_ST}, +/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPEN_ST}, +/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST}, +/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_CONG, MCA_CCB_OPEN_ST}, +/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_RSP_TOUT, MCA_CCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 mca_ccb_st_closing[][MCA_CCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST}, +/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}, +/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tMCA_CCB_ST_TBL)[MCA_CCB_NUM_COLS]; + +/* state table */ +const tMCA_CCB_ST_TBL mca_ccb_st_tbl[] = { + mca_ccb_st_opening, + mca_ccb_st_open, + mca_ccb_st_closing +}; + +#if (BT_TRACE_VERBOSE == TRUE) +/* verbose event strings for trace */ +static const char * const mca_ccb_evt_str[] = { + "API_CONNECT_EVT", + "API_DISCONNECT_EVT", + "API_REQ_EVT", + "API_RSP_EVT", + "MSG_REQ_EVT", + "MSG_RSP_EVT", + "DL_OPEN_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_CONG_EVT", + "RSP_TOUT_EVT" +}; +/* verbose state strings for trace */ +static const char * const mca_ccb_st_str[] = { + "NULL_ST", + "OPENING_ST", + "OPEN_ST", + "CLOSING_ST" +}; +#endif + +/******************************************************************************* +** +** Function mca_stop_timer +** +** Description This function is stop a MCAP timer +** +** This function is for use internal to MCAP only. +** +** Returns void +** +*******************************************************************************/ +void mca_stop_timer(tMCA_CCB *p_ccb) +{ + if (p_ccb->timer_entry.event == BTU_TTYPE_MCA_CCB_RSP) + { + btu_stop_timer(&p_ccb->timer_entry); + p_ccb->timer_entry.event = 0; + } +} + +/******************************************************************************* +** +** Function mca_ccb_event +** +** Description This function is the CCB state machine main function. +** It uses the state and action function tables to execute +** action functions. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CCB_EVT *p_data) +{ + tMCA_CCB_ST_TBL state_table; + UINT8 action; + +#if (BT_TRACE_VERBOSE == TRUE) + MCA_TRACE_EVENT3("CCB ccb=%d event=%s state=%s", mca_ccb_to_hdl(p_ccb), mca_ccb_evt_str[event], mca_ccb_st_str[p_ccb->state]); +#else + MCA_TRACE_EVENT3("CCB ccb=%d event=%d state=%d", mca_ccb_to_hdl(p_ccb), event, p_ccb->state); +#endif + + /* look up the state table for the current state */ + state_table = mca_ccb_st_tbl[p_ccb->state - 1]; + + /* set next state */ + p_ccb->state = state_table[event][MCA_CCB_NEXT_STATE]; + + /* execute action functions */ + if ((action = state_table[event][MCA_CCB_ACT_COL]) != MCA_CCB_IGNORE) + { + (*mca_ccb_action[action])(p_ccb, p_data); + } +} + +/******************************************************************************* +** +** Function mca_ccb_by_bd +** +** Description This function looks up the CCB based on the BD address. +** It returns a pointer to the CCB. +** If no CCB is found it returns NULL. +** +** Returns void. +** +*******************************************************************************/ +tMCA_CCB *mca_ccb_by_bd(tMCA_HANDLE handle, BD_ADDR bd_addr) +{ + tMCA_CCB *p_ccb = NULL; + tMCA_RCB *p_rcb = mca_rcb_by_handle(handle); + tMCA_CCB *p_ccb_tmp; + int i; + + if (p_rcb) + { + i = handle-1; + p_ccb_tmp = &mca_cb.ccb[i*MCA_NUM_LINKS]; + for (i=0; istate != MCA_CCB_NULL_ST && memcmp(p_ccb_tmp->peer_addr, bd_addr, BD_ADDR_LEN) == 0) + { + p_ccb = p_ccb_tmp; + break; + } + } + } + return p_ccb; +} + +/******************************************************************************* +** +** Function mca_ccb_alloc +** +** Description This function allocates a CCB and copies the BD address to +** the CCB. It returns a pointer to the CCB. If no CCB can +** be allocated it returns NULL. +** +** Returns void. +** +*******************************************************************************/ +tMCA_CCB *mca_ccb_alloc(tMCA_HANDLE handle, BD_ADDR bd_addr) +{ + tMCA_CCB *p_ccb = NULL; + tMCA_RCB *p_rcb = mca_rcb_by_handle(handle); + tMCA_CCB *p_ccb_tmp; + int i; + + MCA_TRACE_DEBUG1("mca_ccb_alloc handle:0x%x", handle); + if (p_rcb) + { + i = handle-1; + p_ccb_tmp = &mca_cb.ccb[i*MCA_NUM_LINKS]; + for (i=0; istate == MCA_CCB_NULL_ST) + { + p_ccb_tmp->p_rcb = p_rcb; + p_ccb_tmp->state = MCA_CCB_OPENING_ST; + p_ccb_tmp->cong = TRUE; + memcpy(p_ccb_tmp->peer_addr, bd_addr, BD_ADDR_LEN); + p_ccb = p_ccb_tmp; + break; + } + } + } + return p_ccb; +} + + +/******************************************************************************* +** +** Function mca_ccb_dealloc +** +** Description This function deallocates a CCB. +** +** Returns void. +** +*******************************************************************************/ +void mca_ccb_dealloc(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data) +{ + tMCA_CTRL evt_data; + + MCA_TRACE_DEBUG1("mca_ccb_dealloc ctrl_vpsm:0x%x", p_ccb->ctrl_vpsm); + mca_dcb_close_by_mdl_id (p_ccb, MCA_ALL_MDL_ID); + if (p_ccb->ctrl_vpsm) + { + L2CA_Deregister (p_ccb->ctrl_vpsm); + } + if (p_ccb->data_vpsm) + { + L2CA_Deregister (p_ccb->data_vpsm); + } + mca_free_buf ((void **)&p_ccb->p_rx_msg); + mca_free_buf ((void **)&p_ccb->p_tx_req); + mca_stop_timer(p_ccb); + + if (p_data) + { + /* non-NULL -> an action function -> report disconnect event */ + memcpy (evt_data.disconnect_ind.bd_addr, p_ccb->peer_addr, BD_ADDR_LEN); + evt_data.disconnect_ind.reason = p_data->close.reason; + mca_ccb_report_event(p_ccb, MCA_DISCONNECT_IND_EVT, &evt_data); + } + mca_free_tc_tbl_by_lcid (p_ccb->lcid); + memset (p_ccb, 0, sizeof (tMCA_CCB)); +} + +/******************************************************************************* +** +** Function mca_ccb_to_hdl +** +** Description This function converts a pointer to a CCB to a tMCA_CL +** and returns the value. +** +** Returns void. +** +*******************************************************************************/ +tMCA_CL mca_ccb_to_hdl(tMCA_CCB *p_ccb) +{ + return (UINT8) (p_ccb - mca_cb.ccb + 1); +} + +/******************************************************************************* +** +** Function mca_ccb_by_hdl +** +** Description This function converts an index value to a CCB. It returns +** a pointer to the CCB. If no valid CCB matches the index it +** returns NULL. +** +** Returns void. +** +*******************************************************************************/ +tMCA_CCB *mca_ccb_by_hdl(tMCA_CL mcl) +{ + tMCA_CCB * p_ccb = NULL; + if (mcl && mcl <= MCA_NUM_CCBS && mca_cb.ccb[mcl-1].state) + p_ccb = &mca_cb.ccb[mcl-1]; + return p_ccb; +} + + +/******************************************************************************* +** +** Function mca_ccb_uses_mdl_id +** +** Description This function checkes if a given mdl_id is in use. +** +** Returns TRUE, if the given mdl_id is currently used in the MCL. +** +*******************************************************************************/ +BOOLEAN mca_ccb_uses_mdl_id(tMCA_CCB *p_ccb, UINT16 mdl_id) +{ + BOOLEAN uses = FALSE; + tMCA_DCB *p_dcb; + int i; + + i = mca_ccb_to_hdl(p_ccb)-1; + p_dcb = &mca_cb.dcb[i*MCA_NUM_MDLS]; + for (i=0; istate != MCA_DCB_NULL_ST && p_dcb->mdl_id == mdl_id) + { + uses = TRUE; + break; + } + } + + return uses; +} diff --git a/stack/mcap/mca_dact.c b/stack/mcap/mca_dact.c new file mode 100644 index 0000000..353153e --- /dev/null +++ b/stack/mcap/mca_dact.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP Data Channel Action + * Functions. + * + ******************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "mca_api.h" +#include "mca_int.h" + +/******************************************************************************* +** +** Function mca_dcb_report_cong +** +** Description This function is called to report the congestion flag. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_report_cong (tMCA_DCB *p_dcb) +{ + tMCA_CTRL evt_data; + + evt_data.cong_chg.cong = p_dcb->cong; + evt_data.cong_chg.mdl = mca_dcb_to_hdl(p_dcb); + evt_data.cong_chg.mdl_id = p_dcb->mdl_id; + mca_ccb_report_event (p_dcb->p_ccb, MCA_CONG_CHG_EVT, &evt_data); +} + +/******************************************************************************* +** +** Function mca_dcb_tc_open +** +** Description This function is called to report MCA_OPEN_IND_EVT or +** MCA_OPEN_CFM_EVT event. +** It also clears the congestion flag (dcb.cong). +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_tc_open (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + tMCA_CTRL evt_data; + tMCA_CCB *p_ccb = p_dcb->p_ccb; + UINT8 event = MCA_OPEN_IND_EVT; + + if (p_data->open.param == MCA_INT) + event = MCA_OPEN_CFM_EVT; + p_dcb->cong = FALSE; + evt_data.open_cfm.mtu = p_data->open.peer_mtu; + evt_data.open_cfm.mdl_id = p_dcb->mdl_id; + evt_data.open_cfm.mdl = mca_dcb_to_hdl(p_dcb); + mca_ccb_event (p_ccb, MCA_CCB_DL_OPEN_EVT, NULL); + mca_ccb_report_event (p_ccb, event, &evt_data); +} + +/******************************************************************************* +** +** Function mca_dcb_cong +** +** Description This function sets the congestion state for the DCB. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_cong (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + p_dcb->cong = p_data->llcong; + mca_dcb_report_cong(p_dcb); +} + +/******************************************************************************* +** +** Function mca_dcb_free_data +** +** Description This function frees the received message. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_free_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + GKI_freebuf (p_data); +} + +/******************************************************************************* +** +** Function mca_dcb_do_disconn +** +** Description This function closes a data channel. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_do_disconn (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + tMCA_CLOSE close; + if ((p_dcb->lcid == 0) || (L2CA_DisconnectReq(p_dcb->lcid) == FALSE)) + { + close.param = MCA_INT; + close.reason = L2CAP_DISC_OK; + close.lcid = 0; + mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close); + } +} + +/******************************************************************************* +** +** Function mca_dcb_snd_data +** +** Description This function sends the data from application to the peer device. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_snd_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + UINT8 status; + + /* do not need to check cong, because API already checked the status */ + status = L2CA_DataWrite (p_dcb->lcid, p_data->p_pkt); + if (status == L2CAP_DW_CONGESTED) + { + p_dcb->cong = TRUE; + mca_dcb_report_cong(p_dcb); + } +} + +/******************************************************************************* +** +** Function mca_dcb_hdl_data +** +** Description This function reports the received data through the data +** callback function. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_hdl_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + (*p_dcb->p_cs->p_data_cback) (mca_dcb_to_hdl(p_dcb), (BT_HDR *)p_data); +} + diff --git a/stack/mcap/mca_dsm.c b/stack/mcap/mca_dsm.c new file mode 100644 index 0000000..255b5d4 --- /dev/null +++ b/stack/mcap/mca_dsm.c @@ -0,0 +1,346 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP Data chahnel state machine. + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" + +/***************************************************************************** +** data channel state machine constants and types +*****************************************************************************/ +enum +{ + MCA_DCB_TC_OPEN, + MCA_DCB_CONG, + MCA_DCB_FREE_DATA, + MCA_DCB_DEALLOC, + MCA_DCB_DO_DISCONN, + MCA_DCB_SND_DATA, + MCA_DCB_HDL_DATA, + MCA_DCB_NUM_ACTIONS +}; +#define MCA_DCB_IGNORE MCA_DCB_NUM_ACTIONS + +/* action function list */ +const tMCA_DCB_ACTION mca_dcb_action[] = { + mca_dcb_tc_open, + mca_dcb_cong, + mca_dcb_free_data, + mca_dcb_dealloc, + mca_dcb_do_disconn, + mca_dcb_snd_data, + mca_dcb_hdl_data +}; + +/* state table information */ +#define MCA_DCB_ACTIONS 1 /* number of actions */ +#define MCA_DCB_ACT_COL 0 /* position of action function */ +#define MCA_DCB_NEXT_STATE 1 /* position of next state */ +#define MCA_DCB_NUM_COLS 2 /* number of columns in state tables */ + +/* state table for opening state */ +const UINT8 mca_dcb_st_opening[][MCA_DCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_DO_DISCONN, MCA_DCB_CLOSING_ST}, +/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_OPENING_ST}, +/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_TC_OPEN, MCA_DCB_OPEN_ST}, +/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST}, +/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_CONG, MCA_DCB_OPENING_ST}, +/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_FREE_DATA, MCA_DCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 mca_dcb_st_open[][MCA_DCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_DO_DISCONN, MCA_DCB_CLOSING_ST}, +/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_SND_DATA, MCA_DCB_OPEN_ST}, +/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_IGNORE, MCA_DCB_OPEN_ST}, +/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST}, +/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_CONG, MCA_DCB_OPEN_ST}, +/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_HDL_DATA, MCA_DCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 mca_dcb_st_closing[][MCA_DCB_NUM_COLS] = { +/* Event Action Next State */ +/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST}, +/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST}, +/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_TC_OPEN, MCA_DCB_OPEN_ST}, +/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST}, +/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST}, +/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_FREE_DATA, MCA_DCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tMCA_DCB_ST_TBL)[MCA_DCB_NUM_COLS]; + +/* state table */ +const tMCA_DCB_ST_TBL mca_dcb_st_tbl[] = { + mca_dcb_st_opening, + mca_dcb_st_open, + mca_dcb_st_closing +}; + +#if (BT_TRACE_VERBOSE == TRUE) +/* verbose event strings for trace */ +const char * const mca_dcb_evt_str[] = { + "API_CLOSE_EVT", + "API_WRITE_EVT", + "TC_OPEN_EVT", + "TC_CLOSE_EVT", + "TC_CONG_EVT", + "TC_DATA_EVT" +}; +/* verbose state strings for trace */ +const char * const mca_dcb_st_str[] = { + "NULL_ST", + "OPENING_ST", + "OPEN_ST", + "CLOSING_ST" +}; +#endif + +/******************************************************************************* +** +** Function mca_dcb_event +** +** Description This function is the DCB state machine main function. +** It uses the state and action function tables to execute +** action functions. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_event(tMCA_DCB *p_dcb, UINT8 event, tMCA_DCB_EVT *p_data) +{ + tMCA_DCB_ST_TBL state_table; + UINT8 action; + + if (p_dcb == NULL) + return; +#if (BT_TRACE_VERBOSE == TRUE) + MCA_TRACE_EVENT3("DCB dcb=%d event=%s state=%s", mca_dcb_to_hdl(p_dcb), mca_dcb_evt_str[event], mca_dcb_st_str[p_dcb->state]); +#else + MCA_TRACE_EVENT3("DCB dcb=%d event=%d state=%d", mca_dcb_to_hdl(p_dcb), event, p_dcb->state); +#endif + + /* look up the state table for the current state */ + state_table = mca_dcb_st_tbl[p_dcb->state - 1]; + + /* set next state */ + p_dcb->state = state_table[event][MCA_DCB_NEXT_STATE]; + + /* execute action functions */ + if ((action = state_table[event][MCA_DCB_ACT_COL]) != MCA_DCB_IGNORE) + { + (*mca_dcb_action[action])(p_dcb, p_data); + } +} + +/******************************************************************************* +** +** Function mca_dcb_alloc +** +** Description This function is called to allocate an DCB. +** It initializes the DCB with the data passed to the function. +** +** Returns tMCA_DCB * +** +*******************************************************************************/ +tMCA_DCB *mca_dcb_alloc(tMCA_CCB*p_ccb, tMCA_DEP dep) +{ + tMCA_DCB *p_dcb = NULL, *p_dcb_tmp; + tMCA_RCB *p_rcb = p_ccb->p_rcb; + tMCA_CS *p_cs; + int i, max; + + if (dep < MCA_NUM_DEPS) + { + p_cs = &p_rcb->dep[dep]; + i = mca_ccb_to_hdl(p_ccb)-1; + p_dcb_tmp = &mca_cb.dcb[i*MCA_NUM_MDLS]; + /* make sure p_cs->max_mdl is smaller than MCA_NUM_MDLS at MCA_CreateDep */ + max = p_cs->max_mdl; + for (i=0; istate == MCA_DCB_NULL_ST) + { + p_dcb_tmp->p_ccb = p_ccb; + p_dcb_tmp->state = MCA_DCB_OPENING_ST; + p_dcb_tmp->cong = TRUE; + p_dcb_tmp->p_cs = p_cs; + p_dcb = p_dcb_tmp; + break; + } + } + } + return p_dcb; +} + +/******************************************************************************* +** +** Function mca_dep_free_mdl +** +** Description This function is called to check the number of free mdl for +** the given dep. +** +** Returns the number of free mdl for the given dep +** +*******************************************************************************/ +UINT8 mca_dep_free_mdl(tMCA_CCB *p_ccb, tMCA_DEP dep) +{ + tMCA_DCB *p_dcb; + tMCA_RCB *p_rcb = p_ccb->p_rcb; + tMCA_CS *p_cs; + int i, max; + UINT8 count = 0; + UINT8 left; + + if (dep < MCA_NUM_DEPS) + { + p_cs = &p_rcb->dep[dep]; + i = mca_ccb_to_hdl(p_ccb)-1; + p_dcb = &mca_cb.dcb[i * MCA_NUM_MDLS]; + /* make sure p_cs->max_mdl is smaller than MCA_NUM_MDLS at MCA_CreateDep */ + max = p_cs->max_mdl; + for (i=0; istate != MCA_DCB_NULL_ST) && (p_dcb->p_cs == p_cs)) + { + count++; + break; + } + } + } + else + { + max = 0; + MCA_TRACE_WARNING0("Invalid Dep ID"); + } + left = max - count; + return left; +} + +/******************************************************************************* +** +** Function mca_dcb_dealloc +** +** Description This function deallocates an DCB. +** +** Returns void. +** +*******************************************************************************/ +void mca_dcb_dealloc(tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data) +{ + tMCA_CCB *p_ccb = p_dcb->p_ccb; + UINT8 event = MCA_CLOSE_IND_EVT; + tMCA_CTRL evt_data; + + MCA_TRACE_DEBUG0("mca_dcb_dealloc"); + mca_free_buf ((void **)&p_dcb->p_data); + if (p_data) + { + /* non-NULL -> an action function -> report disconnect event */ + evt_data.close_cfm.mdl = mca_dcb_to_hdl(p_dcb); + evt_data.close_cfm.reason = p_data->close.reason; + evt_data.close_cfm.mdl_id = p_dcb->mdl_id; + if (p_data->close.param == MCA_INT) + event = MCA_CLOSE_CFM_EVT; + if (p_data->close.lcid) + mca_ccb_report_event(p_ccb, event, &evt_data); + } + mca_free_tc_tbl_by_lcid (p_dcb->lcid); + memset (p_dcb, 0, sizeof (tMCA_DCB)); +} + +/******************************************************************************* +** +** Function mca_dcb_to_hdl +** +** Description This function converts a pointer to an DCB to a handle (tMCA_DL). +** It returns the handle. +** +** Returns tMCA_DL. +** +*******************************************************************************/ +tMCA_DL mca_dcb_to_hdl(tMCA_DCB *p_dcb) +{ + return (UINT8) (p_dcb - mca_cb.dcb + 1); +} + +/******************************************************************************* +** +** Function mca_dcb_by_hdl +** +** Description This function finds the DCB for a handle (tMCA_DL). +** It returns a pointer to the DCB. +** If no DCB matches the handle it returns NULL. +** +** Returns tMCA_DCB * +** +*******************************************************************************/ +tMCA_DCB *mca_dcb_by_hdl(tMCA_DL hdl) +{ + tMCA_DCB * p_dcb = NULL; + if (hdl && hdl <= MCA_NUM_DCBS && mca_cb.dcb[hdl-1].state) + p_dcb = &mca_cb.dcb[hdl-1]; + return p_dcb; +} + +/******************************************************************************* +** +** Function mca_dcb_close_by_mdl_id +** +** Description This function finds the DCB for a mdl_id and +** disconnect the mdl +** +** Returns void +** +*******************************************************************************/ +void mca_dcb_close_by_mdl_id(tMCA_CCB*p_ccb, UINT16 mdl_id) +{ + tMCA_DCB *p_dcb; + int i; + + MCA_TRACE_DEBUG1("mca_dcb_close_by_mdl_id mdl_id=%d", mdl_id); + i = mca_ccb_to_hdl(p_ccb)-1; + p_dcb = &mca_cb.dcb[i*MCA_NUM_MDLS]; + for (i=0; istate) + { + if (p_dcb->mdl_id == mdl_id) + { + mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); + break; + } + else if (mdl_id == MCA_ALL_MDL_ID) + { + mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL); + } + } + } +} diff --git a/stack/mcap/mca_int.h b/stack/mcap/mca_int.h new file mode 100644 index 0000000..2563f7f --- /dev/null +++ b/stack/mcap/mca_int.h @@ -0,0 +1,356 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains interfaces which are internal to MCAP. + * + ******************************************************************************/ +#ifndef MCA_INT_H +#define MCA_INT_H +#include "gki.h" +#include "mca_api.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* INT initiates the L2CAP channel */ +#define MCA_ACP 0 +#define MCA_INT 1 + +/***************************************************************************** +** Type Definitions +*****************************************************************************/ + +/* Header structure for api/received request/response. */ +typedef struct { + BT_HDR hdr; /* layer specific information */ + UINT8 op_code; /* the request/response opcode */ + UINT8 rsp_code; /* valid only if op_code is a response */ + UINT16 mdl_id; /* the MDL ID associated with this request/response */ + UINT8 param; /* other parameter */ + UINT8 mdep_id; /* the MDEP ID associated with this request/response */ + /* tMCA_HANDLE rcb_idx; For internal use only */ + /* tMCA_CL ccb_idx; For internal use only */ + tMCA_DL dcb_idx; /* For internal use only */ +} tMCA_CCB_MSG; + +/* This data structure is associated with the AVDT_OPEN_IND_EVT and AVDT_OPEN_CFM_EVT. */ +typedef struct { + BT_HDR hdr; /* Event header */ + UINT16 peer_mtu; /* Transport channel L2CAP MTU of the peer */ + UINT16 lcid; /* L2CAP LCID */ + UINT8 param; +} tMCA_OPEN; + +typedef struct { + UINT16 reason; /* disconnect reason from L2CAP */ + UINT8 param; /* MCA_INT or MCA_ACP */ + UINT16 lcid; /* L2CAP LCID */ +} tMCA_CLOSE; + +/* Header structure for state machine event parameters. */ +typedef union { + BT_HDR hdr; /* layer specific information */ + tMCA_CCB_MSG api; + BOOLEAN llcong; + UINT8 param; + tMCA_OPEN open; + tMCA_CLOSE close; +} tMCA_CCB_EVT; + +/* control channel states */ +enum +{ + MCA_CCB_NULL_ST, /* not allocated */ + MCA_CCB_OPENING_ST, + MCA_CCB_OPEN_ST, /* open */ + MCA_CCB_CLOSING_ST, /* disconnecting */ + MCA_CCB_MAX_ST +}; +typedef UINT8 tMCA_CCB_STATE; + +/* control channel events */ +enum +{ + MCA_CCB_API_CONNECT_EVT, /* application initiates a connect request. */ + MCA_CCB_API_DISCONNECT_EVT, /* application initiates a disconnect request. */ + MCA_CCB_API_REQ_EVT, /* application initiates a request. The request may be create_mdl, delete_mdl, reconnect_mdl or abort_mdl. */ + MCA_CCB_API_RSP_EVT, /* application initiates a create_mdl or reconnect_mdl response. */ + MCA_CCB_MSG_REQ_EVT, /* a create_mdl, delete_mdl, reconnect_mdl or abort_mdl request message is received from the peer. */ + MCA_CCB_MSG_RSP_EVT, /* Response received event. This event is sent whenever a response message is received for an outstanding request message. */ + MCA_CCB_DL_OPEN_EVT, /* data channel open. */ + MCA_CCB_LL_OPEN_EVT, /* Lower layer open. This event is sent when the lower layer channel is open. */ + MCA_CCB_LL_CLOSE_EVT, /* Lower layer close. This event is sent when the lower layer channel is closed. */ + MCA_CCB_LL_CONG_EVT, /* Lower layer congestion. This event is sent when the lower layer is congested. */ + MCA_CCB_RSP_TOUT_EVT /* time out for waiting the message response on the control channel */ +}; + +/* Header structure for callback event parameters. */ +typedef union { + tMCA_OPEN open; + tMCA_CLOSE close; + BT_HDR hdr; /* layer specific information */ + BT_HDR *p_pkt; + BOOLEAN llcong; + UINT16 mdl_id; /* the MDL ID associated with this request/response */ + /* tMCA_HANDLE rcb_idx; For internal use only */ + /* tMCA_CL ccb_idx; For internal use only */ + /* tMCA_DL dcb_idx; For internal use only */ +} tMCA_DCB_EVT; + +/* data channel states */ +enum +{ + MCA_DCB_NULL_ST, /* not allocated */ + MCA_DCB_OPENING_ST, /* create/reconnect sequence is successful, waiting for data channel connection */ + MCA_DCB_OPEN_ST, /* open */ + MCA_DCB_CLOSING_ST, /* disconnecting */ + MCA_DCB_MAX_ST +}; +typedef UINT8 tMCA_DCB_STATE; + +/* data channel events */ +enum +{ + MCA_DCB_API_CLOSE_EVT, /* This event is sent when the application wants to disconnect the data channel.*/ + MCA_DCB_API_WRITE_EVT, /* This event is sent when the application wants to send a data packet to the peer.*/ + MCA_DCB_TC_OPEN_EVT, /* Transport Channel open. This event is sent when the channel is open.*/ + MCA_DCB_TC_CLOSE_EVT, /* Transport Channel close.*/ + MCA_DCB_TC_CONG_EVT, /* Transport Channel congestion status.*/ + MCA_DCB_TC_DATA_EVT /* This event is sent when a data packet is received from the peer.*/ +}; + + + + +/* "states" used in transport channel table */ +#define MCA_TC_ST_UNUSED 0 /* Unused - unallocated */ +#define MCA_TC_ST_IDLE 1 /* No connection */ +#define MCA_TC_ST_ACP 2 /* Waiting to accept a connection */ +#define MCA_TC_ST_INT 3 /* Initiating a connection */ +#define MCA_TC_ST_CONN 4 /* Waiting for connection confirm */ +#define MCA_TC_ST_CFG 5 /* Waiting for configuration complete */ +#define MCA_TC_ST_OPEN 6 /* Channel opened */ +#define MCA_TC_ST_SEC_INT 7 /* Security process as INT */ +#define MCA_TC_ST_SEC_ACP 8 /* Security process as ACP */ + +/* Configuration flags. tMCA_TC_TBL.cfg_flags */ +#define MCA_L2C_CFG_IND_DONE (1<<0) +#define MCA_L2C_CFG_CFM_DONE (1<<1) +#define MCA_L2C_CFG_CONN_INT (1<<2) +#define MCA_L2C_CFG_CONN_ACP (1<<3) +#define MCA_L2C_CFG_DISCN_INT (1<<4) +#define MCA_L2C_CFG_DISCN_ACP (1<<5) + + +#define MCA_CTRL_TCID 0 /* to identify control channel by tMCA_TC_TBL.tcid */ + +/* transport channel table */ +typedef struct { + UINT16 peer_mtu; /* L2CAP mtu of the peer device */ + UINT16 my_mtu; /* Our MTU for this channel */ + UINT16 lcid; /* L2CAP LCID */ + UINT8 tcid; /* transport channel id (0, for control channel. (MDEP ID + 1) for data channel) */ + tMCA_DL cb_idx; /* 1-based index to ccb or dcb */ + UINT8 state; /* transport channel state */ + UINT8 cfg_flags; /* L2CAP configuration flags */ + UINT8 id; /* L2CAP id sent by peer device (need this to handle security pending) */ +} tMCA_TC_TBL; + +/* transport control block */ +typedef struct { + tMCA_TC_TBL tc_tbl[MCA_NUM_TC_TBL]; + UINT8 lcid_tbl[MAX_L2CAP_CHANNELS]; /* map LCID to tc_tbl index */ +} tMCA_TC; + +/* registration control block */ +typedef struct { + tMCA_REG reg; /* the parameter at register */ + tMCA_CS dep[MCA_NUM_DEPS]; /* the registration info for each MDEP */ + tMCA_CTRL_CBACK *p_cback; /* control callback function */ +} tMCA_RCB; + +enum +{ + MCA_CCB_STAT_NORM, /* normal operation (based on ccb state) */ + MCA_CCB_STAT_PENDING, /* waiting for data channel */ + MCA_CCB_STAT_RECONN, /* reinitiate connection after transitioning from CLOSING to IDLE state */ + MCA_CCB_STAT_DISC /* MCA_DisconnectReq or MCA_Deregister is called. waiting for all associated CL and DL to detach */ +}; +typedef UINT8 tMCA_CCB_STAT; + +/* control channel control block */ +/* the ccbs association with the rcbs + * ccb[0] ...ccb[MCA_NUM_LINKS*1-1] -> rcb[0] + * ccb[MCA_NUM_LINKS*1]...ccb[MCA_NUM_LINKS*2-1] -> rcb[1] + * ccb[MCA_NUM_LINKS*2]...ccb[MCA_NUM_LINKS*3-1] -> rcb[2] + */ +typedef struct { + tMCA_RCB *p_rcb; /* the associated registration control block */ + TIMER_LIST_ENT timer_entry; /* CCB timer list entry */ + tMCA_CCB_MSG *p_tx_req; /* Current request being sent/awaiting response */ + tMCA_CCB_MSG *p_rx_msg; /* Current message received/being processed */ + BD_ADDR peer_addr; /* BD address of peer */ + UINT16 sec_mask; /* Security mask for connections as initiator */ + UINT16 ctrl_vpsm; /* The virtual PSM that peer is listening for control channel */ + UINT16 data_vpsm; /* The virtual PSM that peer is listening for data channel. */ + UINT16 lcid; /* L2CAP lcid for this control channel */ + UINT8 state; /* The CCB state machine state */ + BOOLEAN cong; /* Whether control channel is congested */ + tMCA_CCB_STAT status; /* see tMCA_CCB_STAT */ +} tMCA_CCB; +typedef void (*tMCA_CCB_ACTION)(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); + +enum +{ + MCA_DCB_STAT_NORM, /* normal operation (based on dcb state) */ + MCA_DCB_STAT_DEL, /* MCA_Delete is called. waiting for the DL to detach */ + MCA_DCB_STAT_DISC /* MCA_CloseReq is called. waiting for the DL to detach */ +}; +typedef UINT8 tMCA_DCB_STAT; + +/* data channel control block */ +/* the dcbs association with the ccbs + * dcb[0] ...dcb[MCA_NUM_MDLS*1-1] -> ccb[0] + * dcb[MCA_NUM_MDLS*1]...dcb[MCA_NUM_MDLS*2-1] -> ccb[1] + * dcb[MCA_NUM_MDLS*2]...dcb[MCA_NUM_MDLS*3-1] -> ccb[2] + * + * the dcbs association with the rcbs + * dcb[0] ...dcb[MCA_NUM_MDLS*1*MCA_NUM_LINKS*1-1] -> rcb[0] + * dcb[MCA_NUM_MDLS*1*MCA_NUM_LINKS*1]...dcb[MCA_NUM_MDLS*2*MCA_NUM_LINKS*2-1] -> rcb[1] + * dcb[MCA_NUM_MDLS*2*MCA_NUM_LINKS*2]...dcb[MCA_NUM_MDLS*3*MCA_NUM_LINKS*3-1] -> rcb[2] + */ +typedef struct { + tMCA_CCB *p_ccb; /* the associated control control block */ + BT_HDR *p_data; /* data packet held due to L2CAP channel congestion */ + tMCA_CS *p_cs; /* the associated MDEP info. p_cs->type is the mdep id(internal use) */ + const tMCA_CHNL_CFG *p_chnl_cfg; /* cfg params for L2CAP channel */ + UINT16 mdl_id; /* the MDL ID for this data channel */ + UINT16 lcid; /* L2CAP lcid */ + UINT8 state; /* The DCB state machine state */ + BOOLEAN cong; /* Whether data channel is congested */ + tMCA_DCB_STAT status; /* see tMCA_DCB_STAT */ +} tMCA_DCB; + +typedef void (*tMCA_DCB_ACTION)(tMCA_DCB *p_ccb, tMCA_DCB_EVT *p_data); + +/* Control block for MCA */ +typedef struct { + tMCA_RCB rcb[MCA_NUM_REGS]; /* registration control block */ + tMCA_CCB ccb[MCA_NUM_CCBS]; /* control channel control blocks */ + tMCA_DCB dcb[MCA_NUM_DCBS]; /* data channel control blocks */ + tMCA_TC tc; /* transport control block */ + UINT8 trace_level; /* trace level */ +} tMCA_CB; + +/* csm functions */ +extern void mca_ccb_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CCB_EVT *p_data); +extern tMCA_CCB *mca_ccb_by_bd(tMCA_HANDLE handle, BD_ADDR bd_addr); +extern tMCA_CCB *mca_ccb_alloc(tMCA_HANDLE handle, BD_ADDR bd_addr); +extern void mca_ccb_rsp_tout(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_dealloc(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern tMCA_CL mca_ccb_to_hdl(tMCA_CCB *p_ccb); +extern tMCA_CCB *mca_ccb_by_hdl(tMCA_CL mcl); +extern BOOLEAN mca_ccb_uses_mdl_id(tMCA_CCB *p_ccb, UINT16 mdl_id); + +/* cact functions */ +extern void mca_ccb_report_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CTRL *p_data); +extern void mca_ccb_free_msg(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_snd_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_snd_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_do_disconn (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_cong(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_hdl_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_hdl_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_ll_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); +extern void mca_ccb_dl_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data); + +/* dsm functions */ +extern void mca_dcb_event(tMCA_DCB *p_dcb, UINT8 event, tMCA_DCB_EVT *p_data); +extern tMCA_DCB *mca_dcb_alloc(tMCA_CCB*p_ccb, tMCA_DEP dep); +extern UINT8 mca_dep_free_mdl(tMCA_CCB*p_ccb, tMCA_DEP dep); +extern void mca_dcb_dealloc(tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern tMCA_DL mca_dcb_to_hdl(tMCA_DCB *p_dcb); +extern tMCA_DCB *mca_dcb_by_hdl(tMCA_DL hdl); +extern void mca_dcb_close_by_mdl_id(tMCA_CCB*p_ccb, UINT16 mdl_id); + +/* dact functions */ +extern void mca_dcb_tc_open (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern void mca_dcb_cong (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern void mca_dcb_free_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern void mca_dcb_do_disconn (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern void mca_dcb_snd_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); +extern void mca_dcb_hdl_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data); + + +/* main/utils functions */ +extern tMCA_HANDLE mca_handle_by_cpsm(UINT16 psm); +extern tMCA_HANDLE mca_handle_by_dpsm(UINT16 psm); +extern tMCA_TC_TBL * mca_tc_tbl_calloc(tMCA_CCB *p_ccb); +extern tMCA_TC_TBL * mca_tc_tbl_dalloc(tMCA_DCB *p_dcb); +extern tMCA_TC_TBL * mca_tc_tbl_by_lcid(UINT16 lcid); +extern void mca_free_tc_tbl_by_lcid(UINT16 lcid); +extern void mca_set_cfg_by_tbl(tL2CAP_CFG_INFO *p_cfg, tMCA_TC_TBL *p_tbl); +extern void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason); +extern void mca_tc_open_ind(tMCA_TC_TBL *p_tbl); +extern void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested); +extern void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf); +extern tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg); +extern void mca_rcb_dealloc(tMCA_HANDLE handle); +extern tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb); +extern tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle); +extern BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep); +extern void mca_free_buf(void **p_buf); +extern void mca_process_timeout(TIMER_LIST_ENT *p_tle); +extern void mca_stop_timer(tMCA_CCB *p_ccb); + +/* l2c functions */ +extern void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +extern void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +extern UINT16 mca_l2c_open_req(BD_ADDR bd_addr, UINT16 PSM, const tMCA_CHNL_CFG *p_chnl_cfg); + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/****************************************************************************** +** Main Control Block +*******************************************************************************/ +#if MCA_DYNAMIC_MEMORY == FALSE +MCA_API extern tMCA_CB mca_cb; +#else +MCA_API extern tMCA_CB *mca_cb_ptr; +#define mca_cb (*mca_cb_ptr) +#endif + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO mca_l2c_int_appl; +extern const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def; +extern const UINT8 mca_std_msg_len[]; + +#ifdef __cplusplus +} +#endif + +#endif /* MCA_INT_H */ diff --git a/stack/mcap/mca_l2c.c b/stack/mcap/mca_l2c.c new file mode 100644 index 0000000..f8a48bf --- /dev/null +++ b/stack/mcap/mca_l2c.c @@ -0,0 +1,589 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP at L2CAP Interface. + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "btm_api.h" +#include "btm_int.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" + +/* callback function declarations */ +void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void mca_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void mca_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void mca_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void mca_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void mca_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void mca_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void mca_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO mca_l2c_int_appl = { + NULL, + mca_l2c_connect_cfm_cback, + NULL, + mca_l2c_config_ind_cback, + mca_l2c_config_cfm_cback, + mca_l2c_disconnect_ind_cback, + mca_l2c_disconnect_cfm_cback, + NULL, + mca_l2c_data_ind_cback, + mca_l2c_congestion_ind_cback +}; + +/* Control channel eL2CAP default options */ +const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def = { + L2CAP_FCR_ERTM_MODE, /* Mandatory for MCAP */ + MCA_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */ + MCA_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + MCA_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + MCA_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + MCA_FCR_OPT_MPS_SIZE /* MPS segment size */ +}; + + +/******************************************************************************* +** +** Function mca_sec_check_complete_term +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void mca_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tMCA_TC_TBL *p_tbl = (tMCA_TC_TBL *)p_ref_data; + tL2CAP_CFG_INFO cfg; + tL2CAP_ERTM_INFO ertm_info; + + MCA_TRACE_DEBUG1("mca_sec_check_complete_term res: %d", res); + + if ( res == BTM_SUCCESS ) + { + MCA_TRACE_DEBUG2 ("lcid:x%x id:x%x", p_tbl->lcid, p_tbl->id); + /* Set the FCR options: control channel mandates ERTM */ + ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID; + /* Send response to the L2CAP layer. */ + L2CA_ErtmConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK, &ertm_info); + + /* transition to configuration state */ + p_tbl->state = MCA_TC_ST_CFG; + + /* Send L2CAP config req */ + mca_set_cfg_by_tbl (&cfg, p_tbl); + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} + +/******************************************************************************* +** +** Function mca_sec_check_complete_orig +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void mca_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tMCA_TC_TBL *p_tbl = (tMCA_TC_TBL *)p_ref_data; + tL2CAP_CFG_INFO cfg; + + MCA_TRACE_DEBUG1("mca_sec_check_complete_orig res: %d", res); + + if ( res == BTM_SUCCESS ) + { + /* set channel state */ + p_tbl->state = MCA_TC_ST_CFG; + + /* Send L2CAP config req */ + mca_set_cfg_by_tbl (&cfg, p_tbl); + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_DisconnectReq (p_tbl->lcid); + mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} +/******************************************************************************* +** +** Function mca_l2c_cconn_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tMCA_HANDLE handle = mca_handle_by_cpsm(psm); + tMCA_CCB *p_ccb; + tMCA_TC_TBL *p_tbl = NULL; + UINT16 result = L2CAP_CONN_NO_RESOURCES; + tBTM_STATUS rc; + tL2CAP_ERTM_INFO ertm_info, *p_ertm_info = NULL; + tL2CAP_CFG_INFO cfg; + + MCA_TRACE_EVENT3 ("mca_l2c_cconn_ind_cback: lcid:x%x psm:x%x id:x%x", lcid, psm, id); + + /* do we already have a control channel for this peer? */ + if ((p_ccb = mca_ccb_by_bd(handle, bd_addr)) == NULL) + { + /* no, allocate ccb */ + if ((p_ccb = mca_ccb_alloc(handle, bd_addr)) != NULL) + { + /* allocate and set up entry */ + p_ccb->lcid = lcid; + p_tbl = mca_tc_tbl_calloc(p_ccb); + p_tbl->id = id; + p_tbl->cfg_flags= MCA_L2C_CFG_CONN_ACP; + /* proceed with connection */ + /* Check the security */ + rc = btm_sec_mx_access_request (bd_addr, psm, FALSE, BTM_SEC_PROTO_MCA, 0, + &mca_sec_check_complete_term, p_tbl); + if (rc == BTM_CMD_STARTED) + { + /* Set the FCR options: control channel mandates ERTM */ + ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID; + p_ertm_info = &ertm_info; + result = L2CAP_CONN_PENDING; + } + else + result = L2CAP_CONN_OK; + } + + /* deal with simultaneous control channel connect case */ + } + /* else reject their connection */ + + if (!p_tbl || (p_tbl->state != MCA_TC_ST_CFG)) + { + /* Send L2CAP connect rsp */ + L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info); + + /* if result ok, proceed with connection and send L2CAP + config req */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_tbl->state = MCA_TC_ST_CFG; + + /* Send L2CAP config req */ + mca_set_cfg_by_tbl (&cfg, p_tbl); + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + } +} + +/******************************************************************************* +** +** Function mca_l2c_dconn_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tMCA_HANDLE handle = mca_handle_by_dpsm(psm); + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + tMCA_TC_TBL *p_tbl = NULL; + UINT16 result; + tL2CAP_CFG_INFO cfg; + tL2CAP_ERTM_INFO *p_ertm_info = NULL, ertm_info; + const tMCA_CHNL_CFG *p_chnl_cfg; + + MCA_TRACE_EVENT2 ("mca_l2c_dconn_ind_cback: lcid:x%x psm:x%x ", lcid, psm); + + if (((p_ccb = mca_ccb_by_bd(handle, bd_addr)) != NULL) && /* find the CCB */ + (p_ccb->status == MCA_CCB_STAT_PENDING) && /* this CCB is expecting a MDL */ + (p_ccb->p_tx_req && (p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) + { + /* found the associated dcb in listening mode */ + /* proceed with connection */ + p_dcb->lcid = lcid; + p_tbl = mca_tc_tbl_dalloc(p_dcb); + p_tbl->id = id; + p_tbl->cfg_flags= MCA_L2C_CFG_CONN_ACP; + p_chnl_cfg = p_dcb->p_chnl_cfg; + /* assume that control channel has verified the security requirement */ + /* Set the FCR options: control channel mandates ERTM */ + ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode; + ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode); + ertm_info.user_rx_pool_id = p_chnl_cfg->user_rx_pool_id; + ertm_info.user_tx_pool_id = p_chnl_cfg->user_tx_pool_id; + ertm_info.fcr_rx_pool_id = p_chnl_cfg->fcr_rx_pool_id; + ertm_info.fcr_tx_pool_id = p_chnl_cfg->fcr_tx_pool_id; + p_ertm_info = &ertm_info; + result = L2CAP_CONN_OK; + } + else + { + /* else we're not listening for traffic channel; reject + * (this error code is specified by MCAP spec) */ + result = L2CAP_CONN_NO_RESOURCES; + } + + /* Send L2CAP connect rsp */ + L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, result, p_ertm_info); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* transition to configuration state */ + p_tbl->state = MCA_TC_ST_CFG; + + /* Send L2CAP config req */ + mca_set_cfg_by_tbl (&cfg, p_tbl); + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function mca_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tMCA_TC_TBL *p_tbl; + tL2CAP_CFG_INFO cfg; + tMCA_CCB *p_ccb; + + MCA_TRACE_DEBUG2("mca_l2c_connect_cfm_cback lcid: x%x, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + MCA_TRACE_DEBUG2("p_tbl state: %d, tcid: %d", p_tbl->state, p_tbl->tcid); + /* if in correct state */ + if (p_tbl->state == MCA_TC_ST_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + if (p_tbl->tcid != 0) + { + /* set channel state */ + p_tbl->state = MCA_TC_ST_CFG; + + /* Send L2CAP config req */ + mca_set_cfg_by_tbl (&cfg, p_tbl); + L2CA_ConfigReq(lcid, &cfg); + } + else + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + if (p_ccb == NULL) + { + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* set channel state */ + p_tbl->state = MCA_TC_ST_SEC_INT; + p_tbl->lcid = lcid; + p_tbl->cfg_flags= MCA_L2C_CFG_CONN_INT; + + /* Check the security */ + btm_sec_mx_access_request (p_ccb->peer_addr, p_ccb->ctrl_vpsm, + TRUE, BTM_SEC_PROTO_MCA, + p_tbl->tcid, + &mca_sec_check_complete_orig, p_tbl); + } + } + } + + /* failure; notify adaption that channel closed */ + if (result != L2CAP_CONN_OK) + { + p_tbl->cfg_flags |= MCA_L2C_CFG_DISCN_INT; + mca_tc_close_ind(p_tbl, result); + } + } + } +} + +/******************************************************************************* +** +** Function mca_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tMCA_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_tbl->state == MCA_TC_ST_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CONN_OK) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= MCA_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) + { + mca_tc_open_ind(p_tbl); + } + } + /* else failure */ + else + { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function mca_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tMCA_TC_TBL *p_tbl; + UINT16 result = L2CAP_CFG_OK; + + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_tbl->peer_mtu = p_cfg->mtu; + if (p_tbl->peer_mtu < MCA_MIN_MTU) + { + result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + } + } + else + { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } + MCA_TRACE_DEBUG3("peer_mtu: %d, lcid: x%x mtu_present:%d",p_tbl->peer_mtu, lcid, p_cfg->mtu_present); + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = result; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) == 0) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= MCA_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & MCA_L2C_CFG_CFM_DONE) + { + mca_tc_open_ind(p_tbl); + } + } + } +} + +/******************************************************************************* +** +** Function mca_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tMCA_TC_TBL *p_tbl; + UINT16 reason = L2CAP_DISC_TIMEOUT; + + MCA_TRACE_DEBUG2("mca_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d", + lcid, ack_needed); + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_ACP; + if (ack_needed) + reason = L2CAP_DISC_OK; + mca_tc_close_ind(p_tbl, reason); + } +} + +/******************************************************************************* +** +** Function mca_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tMCA_TC_TBL *p_tbl; + + MCA_TRACE_DEBUG2("mca_l2c_disconnect_cfm_cback lcid: x%x, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_INT; + mca_tc_close_ind(p_tbl, result); + } +} + + +/******************************************************************************* +** +** Function mca_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tMCA_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + mca_tc_cong_ind(p_tbl, is_congested); + } +} + +/******************************************************************************* +** +** Function mca_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void mca_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tMCA_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL) + { + mca_tc_data_ind(p_tbl, p_buf); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + + +/******************************************************************************* +** +** Function mca_l2c_open_req +** +** Description This function calls L2CA_ConnectReq() to initiate a L2CAP channel. +** +** Returns void. +** +*******************************************************************************/ +UINT16 mca_l2c_open_req(BD_ADDR bd_addr, UINT16 psm, const tMCA_CHNL_CFG *p_chnl_cfg) +{ + tL2CAP_ERTM_INFO ertm_info; + + if (p_chnl_cfg) + { + ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode; + ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode); + ertm_info.user_rx_pool_id = p_chnl_cfg->user_rx_pool_id; + ertm_info.user_tx_pool_id = p_chnl_cfg->user_tx_pool_id; + ertm_info.fcr_rx_pool_id = p_chnl_cfg->fcr_rx_pool_id; + ertm_info.fcr_tx_pool_id = p_chnl_cfg->fcr_tx_pool_id; + } + else + { + ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID; + } + return L2CA_ErtmConnectReq (psm, bd_addr, &ertm_info); +} + diff --git a/stack/mcap/mca_main.c b/stack/mcap/mca_main.c new file mode 100644 index 0000000..2e05d5e --- /dev/null +++ b/stack/mcap/mca_main.c @@ -0,0 +1,643 @@ +/****************************************************************************** + * + * Copyright (C) 2009-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 implementation file for the MCAP Main Control Block and + * Utility functions. + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "gki.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" +#include "wcassert.h" +#include "l2c_api.h" + +/* Main Control block for MCA */ +#if MCA_DYNAMIC_MEMORY == FALSE +tMCA_CB mca_cb; +#endif + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* table of standard opcode message size */ +const UINT8 mca_std_msg_len[MCA_NUM_STANDARD_OPCODE] = { + 4, /* MCA_OP_ERROR_RSP */ + 5, /* MCA_OP_MDL_CREATE_REQ */ + 5, /* MCA_OP_MDL_CREATE_RSP */ + 3, /* MCA_OP_MDL_RECONNECT_REQ */ + 4, /* MCA_OP_MDL_RECONNECT_RSP */ + 3, /* MCA_OP_MDL_ABORT_REQ */ + 4, /* MCA_OP_MDL_ABORT_RSP */ + 3, /* MCA_OP_MDL_DELETE_REQ */ + 4 /* MCA_OP_MDL_DELETE_RSP */ +}; + + +/******************************************************************************* +** +** Function mca_handle_by_cpsm +** +** Description This function returns the handle for the given control +** channel PSM. 0, if not found. +** +** Returns the MCA handle. +** +*******************************************************************************/ +tMCA_HANDLE mca_handle_by_cpsm(UINT16 psm) +{ + int i; + tMCA_HANDLE handle = 0; + tMCA_RCB *p_rcb = &mca_cb.rcb[0]; + + for (i=0; ip_cback && p_rcb->reg.ctrl_psm == psm) + { + handle = i+1; + break; + } + } + return handle; +} + +/******************************************************************************* +** +** Function mca_handle_by_dpsm +** +** Description This function returns the handle for the given data +** channel PSM. 0, if not found. +** +** Returns the MCA handle. +** +*******************************************************************************/ +tMCA_HANDLE mca_handle_by_dpsm(UINT16 psm) +{ + int i; + tMCA_HANDLE handle = 0; + tMCA_RCB *p_rcb = &mca_cb.rcb[0]; + + for (i=0; ip_cback && p_rcb->reg.data_psm == psm) + { + handle = i+1; + break; + } + } + return handle; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_calloc +** +** Description This function allocates a transport table for the given +** control channel. +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL * mca_tc_tbl_calloc(tMCA_CCB *p_ccb) +{ + tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl; + int i; + + /* find next free entry in tc table */ + for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++) + { + if (p_tbl->state == MCA_TC_ST_UNUSED) + { + break; + } + } + + /* sanity check */ + WC_ASSERT(i != MCA_NUM_TC_TBL); + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags= 0; + p_tbl->cb_idx = mca_ccb_to_hdl(p_ccb); + p_tbl->tcid = MCA_CTRL_TCID; + p_tbl->my_mtu = MCA_CTRL_MTU; + p_tbl->state = MCA_TC_ST_IDLE; + p_tbl->lcid = p_ccb->lcid; + mca_cb.tc.lcid_tbl[p_ccb->lcid - L2CAP_BASE_APPL_CID] = i; + MCA_TRACE_DEBUG1("mca_tc_tbl_calloc cb_idx: %d", p_tbl->cb_idx); + + return p_tbl; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_dalloc +** +** Description This function allocates a transport table for the given +** data channel. +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL * mca_tc_tbl_dalloc(tMCA_DCB *p_dcb) +{ + tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl; + int i; + + /* find next free entry in tc table */ + for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++) + { + if (p_tbl->state == MCA_TC_ST_UNUSED) + { + break; + } + } + + /* sanity check */ + WC_ASSERT(i != MCA_NUM_TC_TBL); + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags= 0; + p_tbl->cb_idx = mca_dcb_to_hdl(p_dcb); + p_tbl->tcid = p_dcb->p_cs->type + 1; + p_tbl->my_mtu = p_dcb->p_chnl_cfg->data_mtu; + p_tbl->state = MCA_TC_ST_IDLE; + p_tbl->lcid = p_dcb->lcid; + mca_cb.tc.lcid_tbl[p_dcb->lcid - L2CAP_BASE_APPL_CID] = i; + MCA_TRACE_DEBUG2("mca_tc_tbl_dalloc tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + + return p_tbl; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_by_lcid +** +** Description Find the transport channel table entry by LCID. +** +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL *mca_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + if (lcid) + { + idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < MCA_NUM_TC_TBL) + { + return &mca_cb.tc.tc_tbl[idx]; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function mca_free_tc_tbl_by_lcid +** +** Description Find the transport table entry by LCID +** and free the tc_tbl +** +** Returns void. +** +*******************************************************************************/ +void mca_free_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + if (lcid) + { + idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < MCA_NUM_TC_TBL) + { + mca_cb.tc.tc_tbl[idx].state = MCA_TC_ST_UNUSED; + } + } +} + + +/******************************************************************************* +** +** Function mca_set_cfg_by_tbl +** +** Description Set the L2CAP configuration information +** +** Returns none. +** +*******************************************************************************/ +void mca_set_cfg_by_tbl(tL2CAP_CFG_INFO *p_cfg, tMCA_TC_TBL *p_tbl) +{ + tMCA_DCB *p_dcb; + const tL2CAP_FCR_OPTS *p_opt; + tMCA_FCS_OPT fcs = MCA_FCS_NONE; + + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_opt = &mca_l2c_fcr_opts_def; + } + else + { + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + p_opt = &p_dcb->p_chnl_cfg->fcr_opt; + fcs = p_dcb->p_chnl_cfg->fcs; + } + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->mtu_present = TRUE; + p_cfg->mtu = p_tbl->my_mtu; + p_cfg->fcr_present = TRUE; + memcpy(&p_cfg->fcr, p_opt, sizeof (tL2CAP_FCR_OPTS)); + if (fcs & MCA_FCS_PRESNT_MASK) + { + p_cfg->fcs_present = TRUE; + p_cfg->fcs = (fcs & MCA_FCS_USE_MASK); + } +} + +/******************************************************************************* +** +** Function mca_tc_close_ind +** +** Description This function is called by the L2CAP interface when the +** L2CAP channel is closed. It looks up the CCB or DCB for +** the channel and sends it a close event. The reason +** parameter is the same value passed by the L2CAP +** callback function. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + tMCA_CLOSE close; + + close.param = MCA_ACP; + close.reason = reason; + close.lcid = p_tbl->lcid; + + MCA_TRACE_DEBUG3("mca_tc_close_ind tcid: %d, cb_idx:%d, old: %d", + p_tbl->tcid, p_tbl->cb_idx, p_tbl->state); + + /* Check if the transport channel is in use */ + if (p_tbl->state == MCA_TC_ST_UNUSED) + return; + + /* clear mca_tc_tbl entry */ + if (p_tbl->cfg_flags&MCA_L2C_CFG_DISCN_INT) + close.param = MCA_INT; + p_tbl->cfg_flags = 0; + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + + /* if control channel, notify ccb that channel close */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + mca_ccb_event(p_ccb, MCA_CCB_LL_CLOSE_EVT, (tMCA_CCB_EVT *)&close); + } + /* notify dcb that channel close */ + else + { + /* look up dcb */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close); + } + } + p_tbl->state = MCA_TC_ST_UNUSED; +} + +/******************************************************************************* +** +** Function mca_tc_open_ind +** +** Description This function is called by the L2CAP interface when +** the L2CAP channel is opened. It looks up the CCB or DCB +** for the channel and sends it an open event. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_open_ind(tMCA_TC_TBL *p_tbl) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + tMCA_OPEN open; + + MCA_TRACE_DEBUG2("mca_tc_open_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + p_tbl->state = MCA_TC_ST_OPEN; + + open.peer_mtu = p_tbl->peer_mtu; + open.lcid = p_tbl->lcid; + /* use param to indicate the role of connection. + * MCA_ACP, if ACP */ + open.param = MCA_INT; + if (p_tbl->cfg_flags & MCA_L2C_CFG_CONN_ACP) + { + open.param = MCA_ACP; + } + + /* if control channel, notify ccb that channel open */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + + mca_ccb_event(p_ccb, MCA_CCB_LL_OPEN_EVT, (tMCA_CCB_EVT *)&open); + } + /* must be data channel, notify dcb that channel open */ + else + { + /* look up dcb */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + + /* put lcid in event data */ + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_OPEN_EVT, (tMCA_DCB_EVT *) &open); + } + } +} + + +/******************************************************************************* +** +** Function mca_tc_cong_ind +** +** Description This function is called by the L2CAP interface layer when +** L2CAP calls the congestion callback. It looks up the CCB +** or DCB for the channel and sends it a congestion event. +** The is_congested parameter is the same value passed by +** the L2CAP callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + + MCA_TRACE_DEBUG2("mca_tc_cong_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + /* if control channel, notify ccb of congestion */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + mca_ccb_event(p_ccb, MCA_CCB_LL_CONG_EVT, (tMCA_CCB_EVT *) &is_congested); + } + /* notify dcb that channel open */ + else + { + /* look up dcb by cb_idx */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_CONG_EVT, (tMCA_DCB_EVT *) &is_congested); + } + } +} + + +/******************************************************************************* +** +** Function mca_tc_data_ind +** +** Description This function is called by the L2CAP interface layer when +** incoming data is received from L2CAP. It looks up the CCB +** or DCB for the channel and routes the data accordingly. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + UINT8 event = MCA_CCB_MSG_RSP_EVT; + UINT8 *p; + UINT8 rej_rsp_code = MCA_RSP_SUCCESS; + + MCA_TRACE_DEBUG2("mca_tc_data_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + + + /* if control channel, handle control message */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + if (p_ccb) + { + p = (UINT8*)(p_buf+1) + p_buf->offset; + /* all the request opcode has bit 0 set. response code has bit 0 clear */ + if ((*p) & 0x01) + event = MCA_CCB_MSG_REQ_EVT; + + if (*p < MCA_NUM_STANDARD_OPCODE) + { + if (p_buf->len != mca_std_msg_len[*p]) + { + MCA_TRACE_ERROR3 ("opcode: %d required len:%d, got len:%d", *p, mca_std_msg_len[*p], p_buf->len); + rej_rsp_code = MCA_RSP_BAD_PARAM; + } + } + else if ((*p >= MCA_FIRST_SYNC_OP) && (*p <= MCA_LAST_SYNC_OP)) + { + MCA_TRACE_ERROR2 ("unsupported SYNC opcode: %d len:%d", *p, p_buf->len); + /* reject unsupported request */ + rej_rsp_code = MCA_RSP_NO_SUPPORT; + } + else + { + MCA_TRACE_ERROR2 ("bad opcode: %d len:%d", *p, p_buf->len); + /* reject unsupported request */ + rej_rsp_code = MCA_RSP_BAD_OPCODE; + } + + p_buf->layer_specific = rej_rsp_code; + /* forward the request/response to state machine */ + mca_ccb_event(p_ccb, event, (tMCA_CCB_EVT *) p_buf); + } /* got a valid ccb */ + else + GKI_freebuf(p_buf); + } + /* else send event to dcb */ + else + { + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_DATA_EVT, (tMCA_DCB_EVT *) p_buf); + } + else + GKI_freebuf(p_buf); + } +} + +/******************************************************************************* +** +** Function mca_rcb_alloc +** +** Description This function allocates a registration control block. +** If no free RCB is available, it returns NULL. +** +** Returns tMCA_RCB * +** +*******************************************************************************/ +tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg) +{ + int i; + tMCA_RCB *p_rcb = NULL; + + for (i=0; ireg, p_reg, sizeof(tMCA_REG)); + break; + } + } + return p_rcb; +} + +/******************************************************************************* +** +** Function mca_rcb_dealloc +** +** Description This function deallocates the RCB with the given handle. +** +** Returns void. +** +*******************************************************************************/ +void mca_rcb_dealloc(tMCA_HANDLE handle) +{ + int i; + BOOLEAN done = TRUE; + tMCA_RCB *p_rcb; + tMCA_CCB *p_ccb; + + if (handle && (handle<=MCA_NUM_REGS)) + { + handle--; + p_rcb = &mca_cb.rcb[handle]; + if (p_rcb->p_cback) + { + p_ccb = &mca_cb.ccb[handle*MCA_NUM_LINKS]; + /* check if all associated CCB are disconnected */ + for (i=0; ip_rcb) + { + done = FALSE; + mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL); + } + } + + if (done) + { + memset (p_rcb, 0, sizeof(tMCA_RCB)); + } + } + } +} + +/******************************************************************************* +** +** Function mca_rcb_to_handle +** +** Description This function converts a pointer to an RCB to +** a handle (tMCA_HANDLE). It returns the handle. +** +** Returns void. +** +*******************************************************************************/ +tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb) +{ + return(UINT8) (p_rcb - mca_cb.rcb + 1); +} + +/******************************************************************************* +** +** Function mca_rcb_by_handle +** +** Description This function finds the RCB for a handle (tMCA_HANDLE). +** It returns a pointer to the RCB. If no RCB matches the +** handle it returns NULL. +** +** Returns tMCA_RCB * +** +*******************************************************************************/ +tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle) +{ + tMCA_RCB *p_rcb = NULL; + + if (handle && (handle<=MCA_NUM_REGS) && mca_cb.rcb[handle-1].p_cback) + { + p_rcb = &mca_cb.rcb[handle-1]; + } + return p_rcb; +} + +/******************************************************************************* +** +** Function mca_is_valid_dep_id +** +** Description This function checks if the given dep_id is valid. +** +** Returns TRUE, if this is a valid local dep_id +** +*******************************************************************************/ +BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep) +{ + BOOLEAN valid = FALSE; + if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback) + { + valid = TRUE; + } + return valid; +} + +/******************************************************************************* +** +** Function mca_free_buf +** +** Description free memory for specified GKI packet +** +** Returns void +** +*******************************************************************************/ +void mca_free_buf (void **p_buf) +{ + if (p_buf && *p_buf) + { + GKI_freebuf(*p_buf); + *p_buf = NULL; + } +} diff --git a/stack/pan/pan_api.c b/stack/pan/pan_api.c new file mode 100644 index 0000000..6aa24b9 --- /dev/null +++ b/stack/pan/pan_api.c @@ -0,0 +1,853 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains main functions to support PAN profile + * commands and events. + * + *****************************************************************************/ + +#include +#include "gki.h" +#include "bt_types.h" +#include "bnep_api.h" +#include "pan_api.h" +#include "pan_int.h" +#include "sdp_api.h" +#include "sdpdefs.h" +#include "l2c_api.h" +#include "hcidefs.h" +#include "btm_api.h" + + +/******************************************************************************* +** +** Function PAN_Register +** +** Description This function is called by the application to register +** its callbacks with PAN profile. The application then +** should set the PAN role explicitly. +** +** Parameters: p_register - contains all callback function pointers +** +** +** Returns none +** +*******************************************************************************/ +void PAN_Register (tPAN_REGISTER *p_register) +{ + BTM_SetDiscoverability (BTM_GENERAL_DISCOVERABLE, 0, 0); + BTM_SetConnectability (BTM_CONNECTABLE, 0, 0); + + pan_register_with_bnep (); + + if (!p_register) + return; + + pan_cb.pan_conn_state_cb = p_register->pan_conn_state_cb; + pan_cb.pan_bridge_req_cb = p_register->pan_bridge_req_cb; + pan_cb.pan_data_buf_ind_cb = p_register->pan_data_buf_ind_cb; + pan_cb.pan_data_ind_cb = p_register->pan_data_ind_cb; + pan_cb.pan_pfilt_ind_cb = p_register->pan_pfilt_ind_cb; + pan_cb.pan_mfilt_ind_cb = p_register->pan_mfilt_ind_cb; + pan_cb.pan_tx_data_flow_cb = p_register->pan_tx_data_flow_cb; + + return; +} + + + +/******************************************************************************* +** +** Function PAN_Deregister +** +** Description This function is called by the application to de-register +** its callbacks with PAN profile. This will make the PAN to +** become inactive. This will deregister PAN services from SDP +** and close all active connections +** +** Parameters: none +** +** +** Returns none +** +*******************************************************************************/ +void PAN_Deregister (void) +{ + pan_cb.pan_bridge_req_cb = NULL; + pan_cb.pan_data_buf_ind_cb = NULL; + pan_cb.pan_data_ind_cb = NULL; + pan_cb.pan_conn_state_cb = NULL; + pan_cb.pan_pfilt_ind_cb = NULL; + pan_cb.pan_mfilt_ind_cb = NULL; + + PAN_SetRole (PAN_ROLE_INACTIVE, NULL, NULL, NULL, NULL); + BNEP_Deregister (); + + return; +} + + + + +/******************************************************************************* +** +** Function PAN_SetRole +** +** Description This function is called by the application to set the PAN +** profile role. This should be called after PAN_Register. +** This can be called any time to change the PAN role +** +** Parameters: role - is bit map of roles to be active +** PAN_ROLE_CLIENT is for PANU role +** PAN_ROLE_GN_SERVER is for GN role +** PAN_ROLE_NAP_SERVER is for NAP role +** sec_mask - Security mask for different roles +** It is array of UINT8. The byte represent the +** security for roles PANU, GN and NAP in order +** p_user_name - Service name for PANU role +** p_gn_name - Service name for GN role +** p_nap_name - Service name for NAP role +** Can be NULL if user wants it to be default +** +** Returns PAN_SUCCESS - if the role is set successfully +** PAN_FAILURE - if the role is not valid +** +*******************************************************************************/ +tPAN_RESULT PAN_SetRole (UINT8 role, + UINT8 *sec_mask, + char *p_user_name, + char *p_gn_name, + char *p_nap_name) +{ + char *p_desc; + UINT8 security[3] = {PAN_PANU_SECURITY_LEVEL, + PAN_GN_SECURITY_LEVEL, + PAN_NAP_SECURITY_LEVEL}; + UINT8 *p_sec; + + /* If the role is not a valid combination reject it */ + if ((!(role & (PAN_ROLE_CLIENT | PAN_ROLE_GN_SERVER | PAN_ROLE_NAP_SERVER))) && + role != PAN_ROLE_INACTIVE) + { + PAN_TRACE_ERROR1 ("PAN role %d is invalid", role); + return PAN_FAILURE; + } + + /* If the current active role is same as the role being set do nothing */ + if (pan_cb.role == role) + { + PAN_TRACE_EVENT1 ("PAN role already was set to: %d", role); + return PAN_SUCCESS; + } + + if (!sec_mask) + p_sec = security; + else + p_sec = sec_mask; + + /* Register all the roles with SDP */ + PAN_TRACE_API1 ("PAN_SetRole() called with role 0x%x", role); +#if (defined (PAN_SUPPORTS_ROLE_NAP) && PAN_SUPPORTS_ROLE_NAP == TRUE) + /* Check the service name */ + if ((p_nap_name == NULL) || (*p_nap_name == 0)) + p_nap_name = PAN_NAP_DEFAULT_SERVICE_NAME; + + if (role & PAN_ROLE_NAP_SERVER) + { + /* Registering for NAP service with SDP */ + p_desc = PAN_NAP_DEFAULT_DESCRIPTION; + + if (pan_cb.pan_nap_sdp_handle != 0) + SDP_DeleteRecord (pan_cb.pan_nap_sdp_handle); + + pan_cb.pan_nap_sdp_handle = pan_register_with_sdp (UUID_SERVCLASS_NAP, p_sec[2], p_nap_name, p_desc); +// btla-specific ++ + bta_sys_add_uuid(UUID_SERVCLASS_NAP); +// btla-specific -- + } + /* If the NAP role is already active and now being cleared delete the record */ + else if (pan_cb.role & PAN_ROLE_NAP_SERVER) + { + if (pan_cb.pan_nap_sdp_handle != 0) + { + SDP_DeleteRecord (pan_cb.pan_nap_sdp_handle); + pan_cb.pan_nap_sdp_handle = 0; +// btla-specific ++ + bta_sys_remove_uuid(UUID_SERVCLASS_NAP); +// btla-specific -- + } + } +#endif + +#if (defined (PAN_SUPPORTS_ROLE_GN) && PAN_SUPPORTS_ROLE_GN == TRUE) + /* Check the service name */ + if ((p_gn_name == NULL) || (*p_gn_name == 0)) + p_gn_name = PAN_GN_DEFAULT_SERVICE_NAME; + + if (role & PAN_ROLE_GN_SERVER) + { + /* Registering for GN service with SDP */ + p_desc = PAN_GN_DEFAULT_DESCRIPTION; + + if (pan_cb.pan_gn_sdp_handle != 0) + SDP_DeleteRecord (pan_cb.pan_gn_sdp_handle); + + pan_cb.pan_gn_sdp_handle = pan_register_with_sdp (UUID_SERVCLASS_GN, p_sec[1], p_gn_name, p_desc); +// btla-specific ++ + bta_sys_add_uuid(UUID_SERVCLASS_GN); +// btla-specific -- + } + /* If the GN role is already active and now being cleared delete the record */ + else if (pan_cb.role & PAN_ROLE_GN_SERVER) + { + if (pan_cb.pan_gn_sdp_handle != 0) + { + SDP_DeleteRecord (pan_cb.pan_gn_sdp_handle); + pan_cb.pan_gn_sdp_handle = 0; +// btla-specific ++ + bta_sys_remove_uuid(UUID_SERVCLASS_GN); +// btla-specific -- + } + } +#endif + +#if (defined (PAN_SUPPORTS_ROLE_PANU) && PAN_SUPPORTS_ROLE_PANU == TRUE) + /* Check the service name */ + if ((p_user_name == NULL) || (*p_user_name == 0)) + p_user_name = PAN_PANU_DEFAULT_SERVICE_NAME; + + if (role & PAN_ROLE_CLIENT) + { + /* Registering for PANU service with SDP */ + p_desc = PAN_PANU_DEFAULT_DESCRIPTION; + if (pan_cb.pan_user_sdp_handle != 0) + SDP_DeleteRecord (pan_cb.pan_user_sdp_handle); + + pan_cb.pan_user_sdp_handle = pan_register_with_sdp (UUID_SERVCLASS_PANU, p_sec[0], p_user_name, p_desc); +// btla-specific ++ + bta_sys_add_uuid(UUID_SERVCLASS_PANU); +// btla-specific -- + } + /* If the PANU role is already active and now being cleared delete the record */ + else if (pan_cb.role & PAN_ROLE_CLIENT) + { + if (pan_cb.pan_user_sdp_handle != 0) + { + SDP_DeleteRecord (pan_cb.pan_user_sdp_handle); + pan_cb.pan_user_sdp_handle = 0; +// btla-specific ++ + bta_sys_remove_uuid(UUID_SERVCLASS_PANU); +// btla-specific -- + } + } +#endif + + /* Check if it is a shutdown request */ + if (role == PAN_ROLE_INACTIVE) + pan_close_all_connections (); + + pan_cb.role = role; + PAN_TRACE_EVENT1 ("PAN role set to: %d", role); + return PAN_SUCCESS; +} + + + +/******************************************************************************* +** +** Function PAN_Connect +** +** Description This function is called by the application to initiate a +** connection to the remote device +** +** Parameters: rem_bda - BD Addr of the remote device +** src_role - Role of the local device for the connection +** dst_role - Role of the remote device for the connection +** PAN_ROLE_CLIENT is for PANU role +** PAN_ROLE_GN_SERVER is for GN role +** PAN_ROLE_NAP_SERVER is for NAP role +** *handle - Pointer for returning Handle to the connection +** +** Returns PAN_SUCCESS - if the connection is initiated successfully +** PAN_NO_RESOURCES - resources are not sufficent +** PAN_FAILURE - if the connection cannot be initiated +** this can be because of the combination of +** src and dst roles may not be valid or +** allowed at that point of time +** +*******************************************************************************/ +tPAN_RESULT PAN_Connect (BD_ADDR rem_bda, UINT8 src_role, UINT8 dst_role, UINT16 *handle) +{ + tPAN_CONN *pcb; + tBNEP_RESULT result; + tBT_UUID src_uuid, dst_uuid; + UINT8 service_id; + UINT32 mx_chan_id; + + /* + ** Initialize the handle so that in case of failure return values + ** the profile will not get confused + */ + *handle = BNEP_INVALID_HANDLE; + + /* Check if PAN is active or not */ + if (!(pan_cb.role & src_role)) + { + PAN_TRACE_ERROR1 ("PAN is not active for the role %d", src_role); + return PAN_FAILURE; + } + + /* Validate the parameters before proceeding */ + if ((src_role != PAN_ROLE_CLIENT && src_role != PAN_ROLE_GN_SERVER && src_role != PAN_ROLE_NAP_SERVER) || + (dst_role != PAN_ROLE_CLIENT && dst_role != PAN_ROLE_GN_SERVER && dst_role != PAN_ROLE_NAP_SERVER)) + { + PAN_TRACE_ERROR2 ("Either source %d or destination role %d is invalid", src_role, dst_role); + return PAN_FAILURE; + } + + /* Check if connection exists for this remote device */ + pcb = pan_get_pcb_by_addr (rem_bda); + + /* If we are PANU for this role validate destination role */ + if (src_role == PAN_ROLE_CLIENT) + { + if ((pan_cb.num_conns > 1) || (pan_cb.num_conns && (!pcb))) + { + /* + ** If the request is not for existing connection reject it + ** because if there is already a connection we cannot accept + ** another connection in PANU role + */ + PAN_TRACE_ERROR0 ("Cannot make PANU connections when there are more than one connection"); + return PAN_INVALID_SRC_ROLE; + } + + src_uuid.uu.uuid16 = UUID_SERVCLASS_PANU; + if (dst_role == PAN_ROLE_CLIENT) + { + service_id = BTM_SEC_SERVICE_BNEP_PANU; + dst_uuid.uu.uuid16 = UUID_SERVCLASS_PANU; + } + else if (dst_role == PAN_ROLE_GN_SERVER) + { + service_id = BTM_SEC_SERVICE_BNEP_GN; + dst_uuid.uu.uuid16 = UUID_SERVCLASS_GN; + } + else + { + service_id = BTM_SEC_SERVICE_BNEP_NAP; + dst_uuid.uu.uuid16 = UUID_SERVCLASS_NAP; + } + mx_chan_id = dst_uuid.uu.uuid16; + } + /* If destination is PANU role validate source role */ + else if (dst_role == PAN_ROLE_CLIENT) + { + if (pan_cb.num_conns && pan_cb.active_role == PAN_ROLE_CLIENT && !pcb) + { + PAN_TRACE_ERROR0 ("Device already have a connection in PANU role"); + return PAN_INVALID_SRC_ROLE; + } + + dst_uuid.uu.uuid16 = UUID_SERVCLASS_PANU; + if (src_role == PAN_ROLE_GN_SERVER) + { + service_id = BTM_SEC_SERVICE_BNEP_GN; + src_uuid.uu.uuid16 = UUID_SERVCLASS_GN; + } + else + { + service_id = BTM_SEC_SERVICE_BNEP_NAP; + src_uuid.uu.uuid16 = UUID_SERVCLASS_NAP; + } + mx_chan_id = src_uuid.uu.uuid16; + } + /* The role combination is not valid */ + else + { + PAN_TRACE_ERROR2 ("Source %d and Destination roles %d are not valid combination", + src_role, dst_role); + return PAN_FAILURE; + } + + /* Allocate control block and initiate connection */ + if (!pcb) + pcb = pan_allocate_pcb (rem_bda, BNEP_INVALID_HANDLE); + if (!pcb) + { + PAN_TRACE_ERROR0 ("PAN Connection failed because of no resources"); + return PAN_NO_RESOURCES; + } + BTM_SetOutService(rem_bda, BTM_SEC_SERVICE_BNEP_PANU, mx_chan_id); + + PAN_TRACE_API6 ("PAN_Connect() for BD Addr %x.%x.%x.%x.%x.%x", + rem_bda[0], rem_bda[1], rem_bda[2], rem_bda[3], rem_bda[4], rem_bda[5]); + if (pcb->con_state == PAN_STATE_IDLE) + { + pan_cb.num_conns++; + } + else if (pcb->con_state == PAN_STATE_CONNECTED) + { + pcb->con_flags |= PAN_FLAGS_CONN_COMPLETED; + } + else + /* PAN connection is still in progress */ + return PAN_WRONG_STATE; + + pcb->con_state = PAN_STATE_CONN_START; + pcb->prv_src_uuid = pcb->src_uuid; + pcb->prv_dst_uuid = pcb->dst_uuid; + + pcb->src_uuid = src_uuid.uu.uuid16; + pcb->dst_uuid = dst_uuid.uu.uuid16; + + src_uuid.len = 2; + dst_uuid.len = 2; + + result = BNEP_Connect (rem_bda, &src_uuid, &dst_uuid, &(pcb->handle)); + if (result != BNEP_SUCCESS) + { + pan_release_pcb (pcb); + return result; + } + + PAN_TRACE_DEBUG1 ("PAN_Connect() current active role set to %d", src_role); + pan_cb.prv_active_role = pan_cb.active_role; + pan_cb.active_role = src_role; + *handle = pcb->handle; + return PAN_SUCCESS; +} + + + + +/******************************************************************************* +** +** Function PAN_Disconnect +** +** Description This is used to disconnect the connection +** +** Parameters: handle - handle for the connection +** +** Returns PAN_SUCCESS - if the connection is closed successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in disconnecting +** +*******************************************************************************/ +tPAN_RESULT PAN_Disconnect (UINT16 handle) +{ + tPAN_CONN *pcb; + tBNEP_RESULT result; + + /* Check if the connection exists */ + pcb = pan_get_pcb_by_handle (handle); + if(!pcb) + { + PAN_TRACE_ERROR1 ("PAN connection not found for the handle %d", handle); + return PAN_FAILURE; + } + + result = BNEP_Disconnect (pcb->handle); + if (pcb->con_state == PAN_STATE_CONNECTED) + pan_cb.num_conns--; + + if (pan_cb.pan_bridge_req_cb && pcb->src_uuid == UUID_SERVCLASS_NAP) + (*pan_cb.pan_bridge_req_cb) (pcb->rem_bda, FALSE); + + pan_release_pcb (pcb); + + if (result != BNEP_SUCCESS) + { + PAN_TRACE_EVENT0 ("Error in closing PAN connection"); + return PAN_FAILURE; + } + + PAN_TRACE_EVENT0 ("PAN connection closed"); + return PAN_SUCCESS; +} + + +/******************************************************************************* +** +** Function PAN_Write +** +** Description This sends data over the PAN connections. If this is called +** on GN or NAP side and the packet is multicast or broadcast +** it will be sent on all the links. Otherwise the correct link +** is found based on the destination address and forwarded on it +** If the return value is not PAN_SUCCESS the application should +** take care of releasing the message buffer +** +** Parameters: handle - handle for the connection +** dst - MAC or BD Addr of the destination device +** src - MAC or BD Addr of the source who sent this packet +** protocol - protocol of the ethernet packet like IP or ARP +** p_data - pointer to the data +** len - length of the data +** ext - to indicate that extension headers present +** +** Returns PAN_SUCCESS - if the data is sent successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in sending data +** +*******************************************************************************/ +tPAN_RESULT PAN_Write (UINT16 handle, BD_ADDR dst, BD_ADDR src, UINT16 protocol, UINT8 *p_data, UINT16 len, BOOLEAN ext) +{ + tPAN_CONN *pcb; + UINT16 i; + tBNEP_RESULT result; + + if (pan_cb.role == PAN_ROLE_INACTIVE || (!(pan_cb.num_conns))) + { + PAN_TRACE_ERROR0 ("PAN is not active Data write failed"); + return PAN_FAILURE; + } + + /* Check if it is broadcast or multicast packet */ + if (dst[0] & 0x01) + { + for (i=0; icon_state != PAN_STATE_CONNECTED) + { + PAN_TRACE_ERROR0 ("PAN Data write when conn is not active"); + return PAN_FAILURE; + } + + result = BNEP_Write (pcb->handle, dst, p_data, len, protocol, src, ext); + if (result == BNEP_IGNORE_CMD) + { + PAN_TRACE_DEBUG0 ("PAN ignored data write to PANU"); + return result; + } + else if (result != BNEP_SUCCESS) + { + PAN_TRACE_ERROR0 ("PAN failed to send data to the PANU"); + return result; + } + + PAN_TRACE_DEBUG0 ("PAN successfully sent data to the PANU"); + return PAN_SUCCESS; +} + + +/******************************************************************************* +** +** Function PAN_WriteBuf +** +** Description This sends data over the PAN connections. If this is called +** on GN or NAP side and the packet is multicast or broadcast +** it will be sent on all the links. Otherwise the correct link +** is found based on the destination address and forwarded on it +** If the return value is not PAN_SUCCESS the application should +** take care of releasing the message buffer +** +** Parameters: handle - handle for the connection +** dst - MAC or BD Addr of the destination device +** src - MAC or BD Addr of the source who sent this packet +** protocol - protocol of the ethernet packet like IP or ARP +** p_buf - pointer to the data buffer +** ext - to indicate that extension headers present +** +** Returns PAN_SUCCESS - if the data is sent successfully +** PAN_FAILURE - if the connection is not found or +** there is an error in sending data +** +*******************************************************************************/ +tPAN_RESULT PAN_WriteBuf (UINT16 handle, BD_ADDR dst, BD_ADDR src, UINT16 protocol, BT_HDR *p_buf, BOOLEAN ext) +{ + tPAN_CONN *pcb; + UINT16 i; + tBNEP_RESULT result; + + /* Check if it is broadcast or multicast packet */ + if (dst[0] & 0x01) + { + UINT8 *p_data; + UINT16 len; + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + len = p_buf->len; + PAN_Write (handle, dst, src, protocol, p_data, len, ext); + GKI_freebuf (p_buf); + return PAN_SUCCESS; + } + + if (pan_cb.role == PAN_ROLE_INACTIVE || (!(pan_cb.num_conns))) + { + PAN_TRACE_ERROR0 ("PAN is not active Data write failed"); + GKI_freebuf (p_buf); + return PAN_FAILURE; + } + + /* Check if the data write is on PANU side */ + if (pan_cb.active_role == PAN_ROLE_CLIENT) + { + /* Data write is on PANU connection */ + for (i=0; icon_state != PAN_STATE_CONNECTED) + { + PAN_TRACE_ERROR0 ("PAN Buf write when conn is not active"); + GKI_freebuf (p_buf); + return PAN_FAILURE; + } + + result = BNEP_WriteBuf (pcb->handle, dst, p_buf, protocol, src, ext); + if (result == BNEP_IGNORE_CMD) + { + PAN_TRACE_DEBUG0 ("PAN ignored data buf write to PANU"); + return result; + } + else if (result != BNEP_SUCCESS) + { + PAN_TRACE_ERROR0 ("PAN failed to send data buf to the PANU"); + return result; + } + + PAN_TRACE_DEBUG0 ("PAN successfully sent data buf to the PANU"); + return PAN_SUCCESS; +} + + +/******************************************************************************* +** +** Function PAN_SetProtocolFilters +** +** Description This function is used to set protocol filters on the peer +** +** Parameters: handle - handle for the connection +** num_filters - number of protocol filter ranges +** start - array of starting protocol numbers +** end - array of ending protocol numbers +** +** +** Returns PAN_SUCCESS if protocol filters are set successfully +** PAN_FAILURE if connection not found or error in setting +** +*******************************************************************************/ +tPAN_RESULT PAN_SetProtocolFilters (UINT16 handle, + UINT16 num_filters, + UINT16 *p_start_array, + UINT16 *p_end_array) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + tPAN_CONN *pcb; + tPAN_RESULT result; + + /* Check if the connection exists */ + pcb = pan_get_pcb_by_handle (handle); + if(!pcb) + { + PAN_TRACE_ERROR1 ("PAN connection not found for the handle %d", handle); + return PAN_FAILURE; + } + + result = BNEP_SetProtocolFilters (pcb->handle, num_filters, p_start_array, p_end_array); + if (result != BNEP_SUCCESS) + { + PAN_TRACE_ERROR1 ("PAN failed to set protocol filters for handle %d", handle); + return result; + } + + PAN_TRACE_API1 ("PAN successfully sent protocol filters for handle %d", handle); + return PAN_SUCCESS; +#else + return PAN_FAILURE; +#endif +} + + + +/******************************************************************************* +** +** Function PAN_SetMulticastFilters +** +** Description This function is used to set multicast filters on the peer +** +** Parameters: handle - handle for the connection +** num_filters - number of multicast filter ranges +** start - array of starting multicast filter addresses +** end - array of ending multicast filter addresses +** +** +** Returns PAN_SUCCESS if multicast filters are set successfully +** PAN_FAILURE if connection not found or error in setting +** +*******************************************************************************/ +tBNEP_RESULT PAN_SetMulticastFilters (UINT16 handle, + UINT16 num_mcast_filters, + UINT8 *p_start_array, + UINT8 *p_end_array) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + tPAN_CONN *pcb; + tPAN_RESULT result; + + /* Check if the connection exists */ + pcb = pan_get_pcb_by_handle (handle); + if(!pcb) + { + PAN_TRACE_ERROR1 ("PAN connection not found for the handle %d", handle); + return PAN_FAILURE; + } + + result = BNEP_SetMulticastFilters (pcb->handle, + num_mcast_filters, p_start_array, p_end_array); + if (result != BNEP_SUCCESS) + { + PAN_TRACE_ERROR1 ("PAN failed to set multicast filters for handle %d", handle); + return result; + } + + PAN_TRACE_API1 ("PAN successfully sent multicast filters for handle %d", handle); + return PAN_SUCCESS; +#else + return PAN_FAILURE; +#endif +} + + +/******************************************************************************* +** +** Function PAN_SetTraceLevel +** +** Description This function sets the trace level for PAN. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 PAN_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + pan_cb.trace_level = new_level; + else + pan_dump_status (); + + return (pan_cb.trace_level); +} + +/******************************************************************************* +** +** Function PAN_Init +** +** Description This function initializes the PAN module variables +** +** Parameters: none +** +** Returns none +** +*******************************************************************************/ +void PAN_Init (void) +{ + memset (&pan_cb, 0, sizeof (tPAN_CB)); + +#if defined(PAN_INITIAL_TRACE_LEVEL) + pan_cb.trace_level = PAN_INITIAL_TRACE_LEVEL; +#else + pan_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif +} + + diff --git a/stack/pan/pan_int.h b/stack/pan/pan_int.h new file mode 100644 index 0000000..4e02236 --- /dev/null +++ b/stack/pan/pan_int.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * + * Copyright (C) 2001-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used PAN definitions + * + ******************************************************************************/ + +#ifndef PAN_INT_H +#define PAN_INT_H + +#include "pan_api.h" + +/* +** This role is used to shutdown the profile. Used internally +** Applications should call PAN_Deregister to shutdown the profile +*/ +#define PAN_ROLE_INACTIVE 0 + +/* Protocols supported by the host internal stack, are registered with SDP */ +#define PAN_PROTOCOL_IP 0x0800 +#define PAN_PROTOCOL_ARP 0x0806 + +#define PAN_PROFILE_VERSION 0x0100 /* Version 1.00 */ + +/* Define the PAN Connection Control Block +*/ +typedef struct +{ +#define PAN_STATE_IDLE 0 +#define PAN_STATE_CONN_START 1 +#define PAN_STATE_CONNECTED 2 + UINT8 con_state; + +#define PAN_FLAGS_CONN_COMPLETED 0x01 + UINT8 con_flags; + + UINT16 handle; + BD_ADDR rem_bda; + + UINT16 bad_pkts_rcvd; + UINT16 src_uuid; + UINT16 dst_uuid; + UINT16 prv_src_uuid; + UINT16 prv_dst_uuid; + UINT16 ip_addr_known; + UINT32 ip_addr; + +} tPAN_CONN; + + +/* The main PAN control block +*/ +typedef struct +{ + UINT8 role; + UINT8 active_role; + UINT8 prv_active_role; + tPAN_CONN pcb[MAX_PAN_CONNS]; + + tPAN_CONN_STATE_CB *pan_conn_state_cb; /* Connection state callback */ + tPAN_BRIDGE_REQ_CB *pan_bridge_req_cb; + tPAN_DATA_IND_CB *pan_data_ind_cb; + tPAN_DATA_BUF_IND_CB *pan_data_buf_ind_cb; + tPAN_FILTER_IND_CB *pan_pfilt_ind_cb; /* protocol filter indication callback */ + tPAN_MFILTER_IND_CB *pan_mfilt_ind_cb; /* multicast filter indication callback */ + tPAN_TX_DATA_FLOW_CB *pan_tx_data_flow_cb; + + BD_ADDR my_bda; /* BD Address of this device */ + char *user_service_name; + char *gn_service_name; + char *nap_service_name; + UINT32 pan_user_sdp_handle; + UINT32 pan_gn_sdp_handle; + UINT32 pan_nap_sdp_handle; + UINT8 num_conns; + UINT8 trace_level; +} tPAN_CB; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global PAN data +*/ +#if PAN_DYNAMIC_MEMORY == FALSE +PAN_API extern tPAN_CB pan_cb; +#else +PAN_API extern tPAN_CB *pan_cb_ptr; +#define pan_cb (*pan_cb_ptr) +#endif + +/*******************************************************************************/ +extern void pan_register_with_bnep (void); +extern void pan_conn_ind_cb (UINT16 handle, + BD_ADDR p_bda, + tBT_UUID *remote_uuid, + tBT_UUID *local_uuid, + BOOLEAN is_role_change); +extern void pan_connect_state_cb (UINT16 handle, BD_ADDR rem_bda, tBNEP_RESULT result, BOOLEAN is_role_change); +extern void pan_data_ind_cb (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + UINT8 *p_data, + UINT16 len, + BOOLEAN fw_ext_present); +extern void pan_data_buf_ind_cb (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + BT_HDR *p_buf, + BOOLEAN ext); +extern void pan_tx_data_flow_cb (UINT16 handle, + tBNEP_RESULT event); +void pan_proto_filt_ind_cb (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters); +void pan_mcast_filt_ind_cb (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters); +extern UINT32 pan_register_with_sdp (UINT16 uuid, UINT8 sec_mask, char *p_name, char *p_desc); +extern tPAN_CONN *pan_allocate_pcb (BD_ADDR p_bda, UINT16 handle); +extern tPAN_CONN *pan_get_pcb_by_handle (UINT16 handle); +extern tPAN_CONN *pan_get_pcb_by_addr (BD_ADDR p_bda); +extern void pan_close_all_connections (void); +extern void pan_release_pcb (tPAN_CONN *p_pcb); +extern void pan_dump_status (void); + +/********************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/pan/pan_main.c b/stack/pan/pan_main.c new file mode 100644 index 0000000..6cbe1ce --- /dev/null +++ b/stack/pan/pan_main.c @@ -0,0 +1,730 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains main functions to support PAN profile + * commands and events. + * + ******************************************************************************/ + +#include +#include "gki.h" +#include "bt_types.h" +#include "bnep_api.h" +#include "pan_api.h" +#include "pan_int.h" +#include "sdp_api.h" +#include "sdpdefs.h" +#include "l2c_api.h" +#include "hcidefs.h" + + +#if PAN_DYNAMIC_MEMORY == FALSE +tPAN_CB pan_cb; +#endif + +#define UUID_CONSTANT_PART 12 +UINT8 constant_pan_uuid[UUID_CONSTANT_PART] = {0, 0, 0x10, 0, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; + + +/******************************************************************************* +** +** Function pan_register_with_bnep +** +** Description This function registers PAN profile with BNEP +** +** Parameters: none +** +** Returns none +** +*******************************************************************************/ +void pan_register_with_bnep (void) +{ + tBNEP_REGISTER reg_info; + + memset (®_info, 0, sizeof (tBNEP_REGISTER)); + + reg_info.p_conn_ind_cb = pan_conn_ind_cb; + reg_info.p_conn_state_cb = pan_connect_state_cb; + reg_info.p_data_buf_cb = pan_data_buf_ind_cb; + reg_info.p_data_ind_cb = NULL; + reg_info.p_tx_data_flow_cb = pan_tx_data_flow_cb; + reg_info.p_filter_ind_cb = pan_proto_filt_ind_cb; + reg_info.p_mfilter_ind_cb = pan_mcast_filt_ind_cb; + + BNEP_Register (®_info); +} + + +/******************************************************************************* +** +** Function pan_conn_ind_cb +** +** Description This function is registered with BNEP as connection indication +** callback. BNEP will call this when there is connection +** request from the peer. PAN should call BNEP_ConnectResp to +** indicate whether to accept the connection or reject +** +** Parameters: handle - handle for the connection +** p_bda - BD Addr of the peer requesting the connection +** remote_uuid - UUID of the source role (peer device role) +** local_uuid - UUID of the destination role (local device role) +** is_role_change - Flag to indicate that it is a role change +** +** Returns none +** +*******************************************************************************/ +void pan_conn_ind_cb (UINT16 handle, + BD_ADDR p_bda, + tBT_UUID *remote_uuid, + tBT_UUID *local_uuid, + BOOLEAN is_role_change) +{ + tPAN_CONN *pcb; + UINT8 req_role; + BOOLEAN wrong_uuid; + + /* + ** If we are in GN or NAP role and have one or more + ** active connections and the received connection is + ** for user role reject it. + ** If we are in user role with one connection active + ** reject the connection. + ** Allocate PCB and store the parameters + ** Make bridge request to the host system if connection + ** is for NAP + */ + wrong_uuid = FALSE; +#if (defined (BNEP_SUPPORTS_ALL_UUID_LENGTHS) && BNEP_SUPPORTS_ALL_UUID_LENGTHS == TRUE) + if (remote_uuid->len == 16) + { + /* + ** If the UUID is 16 bytes forst two bytes should be zeros + ** and last 12 bytes should match the spec defined constant value + */ + if (memcmp (constant_pan_uuid, remote_uuid->uu.uuid128 + 4, UUID_CONSTANT_PART)) + wrong_uuid = TRUE; + + if (remote_uuid->uu.uuid128[0] || remote_uuid->uu.uuid128[1]) + wrong_uuid = TRUE; + + /* Extract the 16 bit equivalent of the UUID */ + remote_uuid->uu.uuid16 = (UINT16)((remote_uuid->uu.uuid128[2] << 8) | remote_uuid->uu.uuid128[3]); + remote_uuid->len = 2; + } + if (remote_uuid->len == 4) + { + /* First two bytes should be zeros */ + if (remote_uuid->uu.uuid32 & 0xFFFF0000) + wrong_uuid = TRUE; + + remote_uuid->uu.uuid16 = (UINT16)remote_uuid->uu.uuid32; + remote_uuid->len = 2; + } + + if (wrong_uuid) + { + PAN_TRACE_ERROR0 ("PAN Connection failed because of wrong remote UUID "); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_SRC_UUID); + return; + } + + wrong_uuid = FALSE; + if (local_uuid->len == 16) + { + /* + ** If the UUID is 16 bytes forst two bytes should be zeros + ** and last 12 bytes should match the spec defined constant value + */ + if (memcmp (constant_pan_uuid, local_uuid->uu.uuid128 + 4, UUID_CONSTANT_PART)) + wrong_uuid = TRUE; + + if (local_uuid->uu.uuid128[0] || local_uuid->uu.uuid128[1]) + wrong_uuid = TRUE; + + /* Extract the 16 bit equivalent of the UUID */ + local_uuid->uu.uuid16 = (UINT16)((local_uuid->uu.uuid128[2] << 8) | local_uuid->uu.uuid128[3]); + local_uuid->len = 2; + } + if (local_uuid->len == 4) + { + /* First two bytes should be zeros */ + if (local_uuid->uu.uuid32 & 0xFFFF0000) + wrong_uuid = TRUE; + + local_uuid->uu.uuid16 = (UINT16)local_uuid->uu.uuid32; + local_uuid->len = 2; + } + + if (wrong_uuid) + { + PAN_TRACE_ERROR0 ("PAN Connection failed because of wrong local UUID "); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_DST_UUID); + return; + } + + PAN_TRACE_EVENT5 ("pan_conn_ind_cb - for handle %d, current role %d, dst uuid 0x%x, src uuid 0x%x, role change %s", + handle, pan_cb.role, local_uuid->uu.uuid16, remote_uuid->uu.uuid16, is_role_change?"YES":"NO"); + /* The acceptable UUID size is only 2 */ + if (remote_uuid->len != 2) + { + PAN_TRACE_ERROR1 ("PAN Connection failed because of wrong UUID size %d", remote_uuid->len); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_UUID_SIZE); + return; + } +#endif + + /* Check if the source UUID is a valid one */ + if (remote_uuid->uu.uuid16 != UUID_SERVCLASS_PANU && + remote_uuid->uu.uuid16 != UUID_SERVCLASS_NAP && + remote_uuid->uu.uuid16 != UUID_SERVCLASS_GN) + { + PAN_TRACE_ERROR1 ("Src UUID 0x%x is not valid", remote_uuid->uu.uuid16); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_SRC_UUID); + return; + } + + /* Check if the destination UUID is a valid one */ + if (local_uuid->uu.uuid16 != UUID_SERVCLASS_PANU && + local_uuid->uu.uuid16 != UUID_SERVCLASS_NAP && + local_uuid->uu.uuid16 != UUID_SERVCLASS_GN) + { + PAN_TRACE_ERROR1 ("Dst UUID 0x%x is not valid", remote_uuid->uu.uuid16); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_DST_UUID); + return; + } + + /* Check if currently we support the destination role requested */ + if (((!(pan_cb.role & UUID_SERVCLASS_PANU)) + && local_uuid->uu.uuid16 == UUID_SERVCLASS_PANU) || + ((!(pan_cb.role & UUID_SERVCLASS_GN)) + && local_uuid->uu.uuid16 == UUID_SERVCLASS_GN) || + ((!(pan_cb.role & UUID_SERVCLASS_NAP)) + && local_uuid->uu.uuid16 == UUID_SERVCLASS_NAP)) + { + PAN_TRACE_ERROR1 ("PAN Connection failed because of unsupported destination UUID 0x%x", local_uuid->uu.uuid16); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_DST_UUID); + return; + } + + /* Requested destination role is */ + if (local_uuid->uu.uuid16 == UUID_SERVCLASS_PANU) + req_role = PAN_ROLE_CLIENT; + else if (local_uuid->uu.uuid16 == UUID_SERVCLASS_GN) + req_role = PAN_ROLE_GN_SERVER; + else + req_role = PAN_ROLE_NAP_SERVER; + + /* If the connection indication is for the existing connection + ** Check if the new destination role is acceptable + */ + pcb = pan_get_pcb_by_handle (handle); + if (pcb) + { + if (pan_cb.num_conns > 1 && local_uuid->uu.uuid16 == UUID_SERVCLASS_PANU) + { + /* There are connections other than this one + ** so we cann't accept PANU role. Reject + */ + PAN_TRACE_ERROR0 ("Dst UUID should be either GN or NAP only because there are other connections"); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_DST_UUID); + return; + } + + /* If it is already in connected state check for bridging status */ + if (pcb->con_state == PAN_STATE_CONNECTED) + { + PAN_TRACE_EVENT2 ("PAN Role changing New Src 0x%x Dst 0x%x", + remote_uuid->uu.uuid16, local_uuid->uu.uuid16); + + pcb->prv_src_uuid = pcb->src_uuid; + pcb->prv_dst_uuid = pcb->dst_uuid; + + if (pcb->src_uuid == UUID_SERVCLASS_NAP && + local_uuid->uu.uuid16 != UUID_SERVCLASS_NAP) + { + /* Remove bridging */ + if (pan_cb.pan_bridge_req_cb) + (*pan_cb.pan_bridge_req_cb) (pcb->rem_bda, FALSE); + } + } + /* Set the latest active PAN role */ + pan_cb.active_role = req_role; + pcb->src_uuid = local_uuid->uu.uuid16; + pcb->dst_uuid = remote_uuid->uu.uuid16; + BNEP_ConnectResp (handle, BNEP_SUCCESS); + return; + } + else + { + /* If this a new connection and destination is PANU role and + ** we already have a connection then reject the request. + ** If we have a connection in PANU role then reject it + */ + if (pan_cb.num_conns && + (local_uuid->uu.uuid16 == UUID_SERVCLASS_PANU || + pan_cb.active_role == PAN_ROLE_CLIENT)) + { + PAN_TRACE_ERROR0 ("PAN already have a connection and can't be user"); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED_DST_UUID); + return; + } + } + + /* This is a new connection */ + PAN_TRACE_DEBUG1 ("New connection indication for handle %d", handle); + pcb = pan_allocate_pcb (p_bda, handle); + if (!pcb) + { + PAN_TRACE_ERROR0 ("PAN no control block for new connection"); + BNEP_ConnectResp (handle, BNEP_CONN_FAILED); + return; + } + + PAN_TRACE_EVENT1 ("PAN connection destination UUID is 0x%x", local_uuid->uu.uuid16); + /* Set the latest active PAN role */ + pan_cb.active_role = req_role; + pcb->src_uuid = local_uuid->uu.uuid16; + pcb->dst_uuid = remote_uuid->uu.uuid16; + pcb->con_state = PAN_STATE_CONN_START; + pan_cb.num_conns++; + + BNEP_ConnectResp (handle, BNEP_SUCCESS); + return; +} + + +/******************************************************************************* +** +** Function pan_connect_state_cb +** +** Description This function is registered with BNEP as connection state +** change callback. BNEP will call this when the connection +** is established successfully or terminated +** +** Parameters: handle - handle for the connection given in the connection +** indication callback +** rem_bda - remote device bd addr +** result - indicates whether the connection is up or down +** BNEP_SUCCESS if the connection is up +** all other values indicates appropriate errors +** is_role_change - flag to indicate that it is a role change +** +** Returns none +** +*******************************************************************************/ +void pan_connect_state_cb (UINT16 handle, BD_ADDR rem_bda, tBNEP_RESULT result, BOOLEAN is_role_change) +{ + tPAN_CONN *pcb; + UINT8 peer_role; + + PAN_TRACE_EVENT2 ("pan_connect_state_cb - for handle %d, result %d", handle, result); + pcb = pan_get_pcb_by_handle (handle); + if (!pcb) + { + PAN_TRACE_ERROR1 ("PAN State change indication for wrong handle %d", handle); + return; + } + + /* If the connection is getting terminated remove bridging */ + if (result != BNEP_SUCCESS) + { + /* Inform the application that connection is down */ + if (pan_cb.pan_conn_state_cb) + (*pan_cb.pan_conn_state_cb) (pcb->handle, pcb->rem_bda, result, is_role_change, PAN_ROLE_INACTIVE, PAN_ROLE_INACTIVE); + + /* Check if this failure is for role change only */ + if (pcb->con_state != PAN_STATE_CONNECTED && + (pcb->con_flags & PAN_FLAGS_CONN_COMPLETED)) + { + /* restore the original values */ + PAN_TRACE_EVENT0 ("restoring the connection state to active"); + pcb->con_state = PAN_STATE_CONNECTED; + pcb->con_flags &= (~PAN_FLAGS_CONN_COMPLETED); + + pcb->src_uuid = pcb->prv_src_uuid; + pcb->dst_uuid = pcb->prv_dst_uuid; + pan_cb.active_role = pan_cb.prv_active_role; + + if ((pcb->src_uuid == UUID_SERVCLASS_NAP) && pan_cb.pan_bridge_req_cb) + (*pan_cb.pan_bridge_req_cb) (pcb->rem_bda, TRUE); + + return; + } + + if (pcb->con_state == PAN_STATE_CONNECTED) + { + /* If the connections destination role is NAP remove bridging */ + if ((pcb->src_uuid == UUID_SERVCLASS_NAP) && pan_cb.pan_bridge_req_cb) + (*pan_cb.pan_bridge_req_cb) (pcb->rem_bda, FALSE); + } + + pan_cb.num_conns--; + pan_release_pcb (pcb); + return; + } + + /* Requested destination role is */ + if (pcb->src_uuid == UUID_SERVCLASS_PANU) + pan_cb.active_role = PAN_ROLE_CLIENT; + else if (pcb->src_uuid == UUID_SERVCLASS_GN) + pan_cb.active_role = PAN_ROLE_GN_SERVER; + else + pan_cb.active_role = PAN_ROLE_NAP_SERVER; + + if (pcb->dst_uuid == UUID_SERVCLASS_PANU) + peer_role = PAN_ROLE_CLIENT; + else if (pcb->dst_uuid == UUID_SERVCLASS_GN) + peer_role = PAN_ROLE_GN_SERVER; + else + peer_role = PAN_ROLE_NAP_SERVER; + + pcb->con_state = PAN_STATE_CONNECTED; + + /* Inform the application that connection is down */ + if (pan_cb.pan_conn_state_cb) + (*pan_cb.pan_conn_state_cb) (pcb->handle, pcb->rem_bda, PAN_SUCCESS, is_role_change, pan_cb.active_role, peer_role); + + /* Create bridge if the destination role is NAP */ + if (pan_cb.pan_bridge_req_cb && pcb->src_uuid == UUID_SERVCLASS_NAP) + { + PAN_TRACE_EVENT0 ("PAN requesting for bridge"); + (*pan_cb.pan_bridge_req_cb) (pcb->rem_bda, TRUE); + } +} + + +/******************************************************************************* +** +** Function pan_data_ind_cb +** +** Description This function is registered with BNEP as data indication +** callback. BNEP will call this when the peer sends any data +** on this connection +** +** Parameters: handle - handle for the connection +** src - source BD Addr +** dst - destination BD Addr +** protocol - Network protocol of the Eth packet +** p_data - pointer to the data +** len - length of the data +** fw_ext_present - to indicate whether the data contains any +** extension headers before the payload +** +** Returns none +** +*******************************************************************************/ +void pan_data_ind_cb (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + UINT8 *p_data, + UINT16 len, + BOOLEAN ext) +{ + tPAN_CONN *pcb; + UINT16 i; + BOOLEAN forward; + + /* + ** Check the connection status + ** If the destination address is MAC broadcast send on all links + ** except on the one received + ** If the destination uuid is for NAP send to host system also + ** If the destination address is one of the devices connected + ** send the packet to over that link + ** If the destination address is unknown and destination uuid is NAP + ** send it to the host system + */ + + PAN_TRACE_EVENT1 ("pan_data_ind_cb - for handle %d", handle); + pcb = pan_get_pcb_by_handle (handle); + if (!pcb) + { + PAN_TRACE_ERROR1 ("PAN Data indication for wrong handle %d", handle); + return; + } + + if (pcb->con_state != PAN_STATE_CONNECTED) + { + PAN_TRACE_ERROR2 ("PAN Data indication in wrong state %d for handle %d", + pcb->con_state, handle); + return; + } + + /* Check if it is broadcast packet */ + if (dst[0] & 0x01) + { + PAN_TRACE_DEBUG2 ("PAN received broadcast packet on handle %d, src uuid 0x%x", + handle, pcb->src_uuid); + for (i=0; isrc_uuid == pan_cb.pcb[i].src_uuid) + { + BNEP_Write (pan_cb.pcb[i].handle, dst, p_data, len, protocol, src, ext); + } + } + + if (pan_cb.pan_data_ind_cb) + (*pan_cb.pan_data_ind_cb) (pcb->handle, src, dst, protocol, p_data, len, ext, TRUE); + + return; + } + + /* Check if it is for any other PAN connection */ + for (i=0; isrc_uuid == pan_cb.pcb[i].src_uuid) + { + if (memcmp (pan_cb.pcb[i].rem_bda, dst, BD_ADDR_LEN) == 0) + { + BNEP_Write (pan_cb.pcb[i].handle, dst, p_data, len, protocol, src, ext); + return; + } + } + } + + if (pcb->src_uuid == UUID_SERVCLASS_NAP) + forward = TRUE; + else + forward = FALSE; + + /* Send it over the LAN or give it to host software */ + if (pan_cb.pan_data_ind_cb) + (*pan_cb.pan_data_ind_cb) (pcb->handle, src, dst, protocol, p_data, len, ext, forward); + + return; +} + + +/******************************************************************************* +** +** Function pan_data_buf_ind_cb +** +** Description This function is registered with BNEP as data buffer indication +** callback. BNEP will call this when the peer sends any data +** on this connection. PAN is responsible to release the buffer +** +** Parameters: handle - handle for the connection +** src - source BD Addr +** dst - destination BD Addr +** protocol - Network protocol of the Eth packet +** p_buf - pointer to the data buffer +** ext - to indicate whether the data contains any +** extension headers before the payload +** +** Returns none +** +*******************************************************************************/ +void pan_data_buf_ind_cb (UINT16 handle, + UINT8 *src, + UINT8 *dst, + UINT16 protocol, + BT_HDR *p_buf, + BOOLEAN ext) +{ + tPAN_CONN *pcb, *dst_pcb; + tBNEP_RESULT result; + UINT16 i, len; + UINT8 *p_data; + BOOLEAN forward = FALSE; + + /* Check if the connection is in right state */ + pcb = pan_get_pcb_by_handle (handle); + if (!pcb) + { + PAN_TRACE_ERROR1 ("PAN Data buffer indication for wrong handle %d", handle); + GKI_freebuf (p_buf); + return; + } + + if (pcb->con_state != PAN_STATE_CONNECTED) + { + PAN_TRACE_ERROR2 ("PAN Data indication in wrong state %d for handle %d", + pcb->con_state, handle); + GKI_freebuf (p_buf); + return; + } + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + len = p_buf->len; + + PAN_TRACE_EVENT4 ("pan_data_buf_ind_cb - for handle %d, protocol 0x%x, length %d, ext %d", + handle, protocol, len, ext); + + if (pcb->src_uuid == UUID_SERVCLASS_NAP) + forward = TRUE; + else + forward = FALSE; + + /* Check if it is broadcast or multicast packet */ + if (pcb->src_uuid != UUID_SERVCLASS_PANU) + { + if (dst[0] & 0x01) + { + PAN_TRACE_DEBUG2 ("PAN received broadcast packet on handle %d, src uuid 0x%x", + handle, pcb->src_uuid); + for (i=0; isrc_uuid == pan_cb.pcb[i].src_uuid) + { + BNEP_Write (pan_cb.pcb[i].handle, dst, p_data, len, protocol, src, ext); + } + } + + if (pan_cb.pan_data_buf_ind_cb) + (*pan_cb.pan_data_buf_ind_cb) (pcb->handle, src, dst, protocol, p_buf, ext, forward); + else if (pan_cb.pan_data_ind_cb) + { + (*pan_cb.pan_data_ind_cb) (pcb->handle, src, dst, protocol, p_data, len, ext, forward); + GKI_freebuf (p_buf); + } + + return; + } + + /* Check if it is for any other PAN connection */ + dst_pcb = pan_get_pcb_by_addr (dst); + if (dst_pcb) + { + PAN_TRACE_EVENT0 ("pan_data_buf_ind_cb - destination PANU found and sending the data"); + result = BNEP_WriteBuf (dst_pcb->handle, dst, p_buf, protocol, src, ext); + if (result != BNEP_SUCCESS && result != BNEP_IGNORE_CMD) + PAN_TRACE_ERROR1 ("Failed to write data for PAN connection handle %d", dst_pcb->handle); + return; + } + } + + /* Send it over the LAN or give it to host software */ + if (pan_cb.pan_data_buf_ind_cb) + (*pan_cb.pan_data_buf_ind_cb) (pcb->handle, src, dst, protocol, p_buf, ext, forward); + else if (pan_cb.pan_data_ind_cb) + { + (*pan_cb.pan_data_ind_cb) (pcb->handle, src, dst, protocol, p_data, len, ext, forward); + GKI_freebuf (p_buf); + } + else + GKI_freebuf (p_buf); + + return; +} + +/******************************************************************************* +** +** Function pan_proto_filt_ind_cb +** +** Description This function is registered with BNEP to receive tx data +** flow status +** +** Parameters: handle - handle for the connection +** event - flow status +** +** Returns none +** +*******************************************************************************/ +void pan_tx_data_flow_cb (UINT16 handle, + tBNEP_RESULT event) +{ + + if (pan_cb.pan_tx_data_flow_cb) + (*pan_cb.pan_tx_data_flow_cb) (handle, event); + + return; +} + +/******************************************************************************* +** +** Function pan_proto_filt_ind_cb +** +** Description This function is registered with BNEP as proto filter indication +** callback. BNEP will call this when the peer sends any protocol +** filter set for the connection or to indicate the result of the +** protocol filter set by the local device +** +** Parameters: handle - handle for the connection +** indication - TRUE if this is indication +** FALSE if it is called to give the result of local +** device protocol filter set +** result - This gives the result of the filter set operation +** num_filters - number of filters set by the peer device +** p_filters - pointer to the filters set by the peer device +** +** Returns none +** +*******************************************************************************/ +void pan_proto_filt_ind_cb (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters) +{ +#if (defined (BNEP_SUPPORTS_PROT_FILTERS) && BNEP_SUPPORTS_PROT_FILTERS == TRUE) + PAN_TRACE_EVENT4 ("pan_proto_filt_ind_cb - called for handle %d with ind %d, result %d, num %d", + handle, indication, result, num_filters); + + if (pan_cb.pan_pfilt_ind_cb) + (*pan_cb.pan_pfilt_ind_cb) (handle, indication, result, num_filters, p_filters); +#endif + + return; +} + + +/******************************************************************************* +** +** Function pan_mcast_filt_ind_cb +** +** Description This function is registered with BNEP as mcast filter indication +** callback. BNEP will call this when the peer sends any multicast +** filter set for the connection or to indicate the result of the +** multicast filter set by the local device +** +** Parameters: handle - handle for the connection +** indication - TRUE if this is indication +** FALSE if it is called to give the result of local +** device multicast filter set +** result - This gives the result of the filter set operation +** num_filters - number of filters set by the peer device +** p_filters - pointer to the filters set by the peer device +** +** Returns none +** +*******************************************************************************/ +void pan_mcast_filt_ind_cb (UINT16 handle, + BOOLEAN indication, + tBNEP_RESULT result, + UINT16 num_filters, + UINT8 *p_filters) +{ +#if (defined (BNEP_SUPPORTS_MULTI_FILTERS) && BNEP_SUPPORTS_MULTI_FILTERS == TRUE) + PAN_TRACE_EVENT4 ("pan_mcast_filt_ind_cb - called for handle %d with ind %d, result %d, num %d", + handle, indication, result, num_filters); + + if (pan_cb.pan_mfilt_ind_cb) + (*pan_cb.pan_mfilt_ind_cb) (handle, indication, result, num_filters, p_filters); +#endif + + return; +} + diff --git a/stack/pan/pan_utils.c b/stack/pan/pan_utils.c new file mode 100644 index 0000000..9ebbb75 --- /dev/null +++ b/stack/pan/pan_utils.c @@ -0,0 +1,351 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains main functions to support PAN profile + * commands and events. + * + *****************************************************************************/ + +#include +#include +#include "gki.h" +#include "bnep_api.h" +#include "pan_api.h" +#include "pan_int.h" +#include "sdp_api.h" +#include "sdpdefs.h" +#include "l2c_api.h" +#include "hcidefs.h" +#include "btm_api.h" + + +static const UINT8 pan_proto_elem_data[] = { + 0x35, 0x18, /* data element sequence of length 0x18 bytes */ + 0x35, 0x06, /* data element sequence for L2CAP descriptor */ + 0x19, 0x01, 0x00, /* UUID for L2CAP - 0x0100 */ + 0x09, 0x00, 0x0F, /* PSM for BNEP - 0x000F */ + 0x35, 0x0E, /* data element seqence for BNEP descriptor */ + 0x19, 0x00, 0x0F, /* UUID for BNEP - 0x000F */ + 0x09, 0x01, 0x00, /* BNEP specific parameter 0 -- Version of BNEP = version 1 = 0x0001 */ + 0x35, 0x06, /* BNEP specific parameter 1 -- Supported network packet type list */ + 0x09, 0x08, 0x00, /* network packet type IPv4 = 0x0800 */ + 0x09, 0x08, 0x06 /* network packet type ARP = 0x0806 */ +}; + +/******************************************************************************* +** +** Function pan_register_with_sdp +** +** Description +** +** Returns +** +*******************************************************************************/ +UINT32 pan_register_with_sdp (UINT16 uuid, UINT8 sec_mask, char *p_name, char *p_desc) +{ + UINT32 sdp_handle; + UINT16 browse_list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT16 security = 0; + UINT8 availability; + UINT32 proto_len = (UINT32)pan_proto_elem_data[1]; + + /* Create a record */ + sdp_handle = SDP_CreateRecord (); + + if (sdp_handle == 0) + { + PAN_TRACE_ERROR0 ("PAN_SetRole - could not create SDP record"); + return 0; + } + + /* Service Class ID List */ + SDP_AddServiceClassIdList (sdp_handle, 1, &uuid); + + /* Add protocol element sequence from the constant string */ + SDP_AddAttribute (sdp_handle, ATTR_ID_PROTOCOL_DESC_LIST, DATA_ELE_SEQ_DESC_TYPE, + proto_len, (UINT8 *)(pan_proto_elem_data+2)); + +// btla-specific ++ +#if 0 + availability = 0xFF; + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_AVAILABILITY, UINT_DESC_TYPE, 1, &availability); +#endif +// btla-specific -- + + /* Language base */ + SDP_AddLanguageBaseAttrIDList (sdp_handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID); + + /* Profile descriptor list */ + SDP_AddProfileDescriptorList (sdp_handle, uuid, PAN_PROFILE_VERSION); + + /* Service Name */ + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, + (UINT8) (strlen(p_name) + 1), (UINT8 *)p_name); + + /* Service description */ + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, + (UINT8) (strlen(p_desc) + 1), (UINT8 *)p_desc); + + /* Security description */ + if (sec_mask) + { + UINT16_TO_BE_FIELD(&security, 0x0001); + } + SDP_AddAttribute (sdp_handle, ATTR_ID_SECURITY_DESCRIPTION, UINT_DESC_TYPE, 2, (UINT8 *)&security); + +#if (defined (PAN_SUPPORTS_ROLE_NAP) && PAN_SUPPORTS_ROLE_NAP == TRUE) + if (uuid == UUID_SERVCLASS_NAP) + { + UINT16 NetAccessType = 0x0005; /* Ethernet */ + UINT32 NetAccessRate = 0x0001312D0; /* 10Mb/sec */ + UINT8 array[10], *p; + + /* Net access type. */ + p = array; + UINT16_TO_BE_STREAM (p, NetAccessType); + SDP_AddAttribute (sdp_handle, ATTR_ID_NET_ACCESS_TYPE, UINT_DESC_TYPE, 2, array); + + /* Net access rate. */ + p = array; + UINT32_TO_BE_STREAM (p, NetAccessRate); + SDP_AddAttribute (sdp_handle, ATTR_ID_MAX_NET_ACCESS_RATE, UINT_DESC_TYPE, 4, array); + + /* Register with Security Manager for the specific security level */ + if ((!BTM_SetSecurityLevel (TRUE, p_name, BTM_SEC_SERVICE_BNEP_NAP, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_NAP)) + || (!BTM_SetSecurityLevel (FALSE, p_name, BTM_SEC_SERVICE_BNEP_NAP, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_NAP))) + { + PAN_TRACE_ERROR0 ("PAN Security Registration failed for PANU"); + } + } +#endif +#if (defined (PAN_SUPPORTS_ROLE_GN) && PAN_SUPPORTS_ROLE_GN == TRUE) + if (uuid == UUID_SERVCLASS_GN) + { + if ((!BTM_SetSecurityLevel (TRUE, p_name, BTM_SEC_SERVICE_BNEP_GN, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_GN)) + || (!BTM_SetSecurityLevel (FALSE, p_name, BTM_SEC_SERVICE_BNEP_GN, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_GN))) + { + PAN_TRACE_ERROR0 ("PAN Security Registration failed for GN"); + } + } +#endif +#if (defined (PAN_SUPPORTS_ROLE_PANU) && PAN_SUPPORTS_ROLE_PANU == TRUE) + if (uuid == UUID_SERVCLASS_PANU) + { + if ((!BTM_SetSecurityLevel (TRUE, p_name, BTM_SEC_SERVICE_BNEP_PANU, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_PANU)) + || (!BTM_SetSecurityLevel (FALSE, p_name, BTM_SEC_SERVICE_BNEP_PANU, + sec_mask, BT_PSM_BNEP, BTM_SEC_PROTO_BNEP, UUID_SERVCLASS_PANU))) + { + PAN_TRACE_ERROR0 ("PAN Security Registration failed for PANU"); + } + } +#endif + + /* Make the service browsable */ + SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_list); + + + return sdp_handle; +} + + + +/******************************************************************************* +** +** Function pan_allocate_pcb +** +** Description +** +** Returns +** +*******************************************************************************/ +tPAN_CONN *pan_allocate_pcb (BD_ADDR p_bda, UINT16 handle) +{ + UINT16 i; + + for (i=0; icon_state = PAN_STATE_IDLE; +} + + +/******************************************************************************* +** +** Function pan_dump_status +** +** Description This function dumps the pan control block and connection +** blocks information +** +** Returns none +** +*******************************************************************************/ +void pan_dump_status (void) +{ +#if (defined (PAN_SUPPORTS_DEBUG_DUMP) && PAN_SUPPORTS_DEBUG_DUMP == TRUE) + UINT16 i; + char buff[200]; + tPAN_CONN *p_pcb; + + PAN_TRACE_DEBUG3 ("PAN role %x, active role %d, num_conns %d", + pan_cb.role, pan_cb.active_role, pan_cb.num_conns); + + for (i = 0, p_pcb = pan_cb.pcb; i < MAX_PAN_CONNS; i++, p_pcb++) + { + sprintf (buff, "%d state %d, handle %d, src 0x%x, dst 0x%x, BD %x.%x.%x.%x.%x.%x", + i, p_pcb->con_state, p_pcb->handle, p_pcb->src_uuid, p_pcb->dst_uuid, + p_pcb->rem_bda[0], p_pcb->rem_bda[1], p_pcb->rem_bda[2], + p_pcb->rem_bda[3], p_pcb->rem_bda[4], p_pcb->rem_bda[5]); + + PAN_TRACE_DEBUG0 (buff); + } +#endif +} + + + diff --git a/stack/rfcomm/port_api.c b/stack/rfcomm/port_api.c new file mode 100644 index 0000000..617d31b --- /dev/null +++ b/stack/rfcomm/port_api.c @@ -0,0 +1,1730 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the Serial Port API code + * + ******************************************************************************/ + +#include +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "btm_int.h" +#include "btm_api.h" +#include "rfc_int.h" +#include "l2c_api.h" +#include "sdp_api.h" + +/* duration of break in 200ms units */ +#define PORT_BREAK_DURATION 1 + +#include +#define info(fmt, ...) ALOGI ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define debug(fmt, ...) ALOGD ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define error(fmt, ...) ALOGE ("## ERROR : %s: " fmt "##",__FUNCTION__, ## __VA_ARGS__) +#define asrt(s) if(!(s)) ALOGE ("## %s assert %s failed at line:%d ##",__FUNCTION__, #s, __LINE__) + +/******************************************************************************* +** +** Function RFCOMM_CreateConnection +** +** Description RFCOMM_CreateConnection function is used from the application +** to establish serial port connection to the peer device, +** or allow RFCOMM to accept a connection from the peer +** application. +** +** Parameters: scn - Service Channel Number as registered with +** the SDP (server) or obtained using SDP from +** the peer device (client). +** is_server - TRUE if requesting application is a server +** mtu - Maximum frame size the application can accept +** bd_addr - BD_ADDR of the peer (client) +** mask - specifies events to be enabled. A value +** of zero disables all events. +** p_handle - OUT pointer to the handle. +** p_mgmt_cb - pointer to callback function to receive +** connection up/down events. +** Notes: +** +** Server can call this function with the same scn parameter multiple times if +** it is ready to accept multiple simulteneous connections. +** +** DLCI for the connection is (scn * 2 + 1) if client originates connection on +** existing none initiator multiplexer channel. Otherwise it is (scn * 2). +** For the server DLCI can be changed later if client will be calling it using +** (scn * 2 + 1) dlci. +** +*******************************************************************************/ +int RFCOMM_CreateConnection (UINT16 uuid, UINT8 scn, BOOLEAN is_server, + UINT16 mtu, BD_ADDR bd_addr, UINT16 *p_handle, + tPORT_CALLBACK *p_mgmt_cb) +{ + tPORT *p_port; + int i; + UINT8 dlci; + tRFC_MCB *p_mcb = port_find_mcb (bd_addr); + UINT16 rfcomm_mtu; + + RFCOMM_TRACE_API3 ("RFCOMM_CreateConnection() called SCN: %d is_server:%d mtu:%d", + scn, is_server, mtu); + RFCOMM_TRACE_API6 ("RFCOMM_CreateConnection() BDA: %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + *p_handle = 0; + + if (( scn == 0 )||(scn >= PORT_MAX_RFC_PORTS )) + { + /* Server Channel Number(SCN) should be in range 1...30 */ + RFCOMM_TRACE_ERROR0 ("RFCOMM_CreateConnection - invalid SCN"); + return (PORT_INVALID_SCN); + } + + /* For client that originate connection on the existing none initiator */ + /* multiplexer channel DLCI should be odd */ + if (p_mcb && !p_mcb->is_initiator && !is_server) + dlci = (scn << 1) + 1; + else + dlci = (scn << 1); + + /* For the server side always allocate a new port. On the client side */ + /* do not allow the same (dlci, bd_addr) to be opened twice by application */ + if (!is_server && ((p_port = port_find_port (dlci, bd_addr)) != NULL)) + { + /* if existing port is also a client port */ + if (p_port->is_server == FALSE) + { + RFCOMM_TRACE_ERROR3 ("RFCOMM_CreateConnection - already opened state:%d, RFC state:%d, MCB state:%d", + p_port->state, p_port->rfc.state, p_port->rfc.p_mcb ? p_port->rfc.p_mcb->state : 0); + return (PORT_ALREADY_OPENED); + } + } + + if ((p_port = port_allocate_port (dlci, bd_addr)) == NULL) + { + RFCOMM_TRACE_WARNING0 ("RFCOMM_CreateConnection - no resources"); + return (PORT_NO_RESOURCES); + } + + p_port->default_signal_state = (PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + switch (uuid) + { + case UUID_PROTOCOL_OBEX: + p_port->default_signal_state = PORT_OBEX_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_SERIAL_PORT: + p_port->default_signal_state = PORT_SPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_LAN_ACCESS_USING_PPP: + p_port->default_signal_state = PORT_PPP_DEFAULT_SIGNAL_STATE; + break; + case UUID_SERVCLASS_DIALUP_NETWORKING: + case UUID_SERVCLASS_FAX: + p_port->default_signal_state = PORT_DUN_DEFAULT_SIGNAL_STATE; + break; + } + + RFCOMM_TRACE_EVENT2 ("RFCOMM_CreateConnection dlci:%d signal state:0x%x", dlci, p_port->default_signal_state); + + *p_handle = p_port->inx; + + p_port->state = PORT_STATE_OPENING; + p_port->uuid = uuid; + p_port->is_server = is_server; + p_port->scn = scn; + p_port->ev_mask = 0; + + /* If the MTU is not specified (0), keep MTU decision until the + * PN frame has to be send + * at that time connection should be established and we + * will know for sure our prefered MTU + */ + + rfcomm_mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + + if (mtu) + p_port->mtu = (mtu < rfcomm_mtu) ? mtu : rfcomm_mtu; + else + p_port->mtu = rfcomm_mtu; + + /* server doesn't need to release port when closing */ + if( is_server ) + { + p_port->keep_port_handle = TRUE; + + /* keep mtu that user asked, p_port->mtu could be updated during param negotiation */ + p_port->keep_mtu = p_port->mtu; + } + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + p_port->local_ctrl.fc = FALSE; + + p_port->p_mgmt_callback = p_mgmt_cb; + + for (i = 0; i < BD_ADDR_LEN; i++) + p_port->bd_addr[i] = bd_addr[i]; + + /* If this is not initiator of the connection need to just wait */ + if (p_port->is_server) + { + return (PORT_SUCCESS); + } + + /* Open will be continued after security checks are passed */ + return port_open_continue (p_port); +} + + +/******************************************************************************* +** +** Function RFCOMM_RemoveConnection +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveConnection (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("RFCOMM_RemoveConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_RemoveConnection() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_RemoveConnection() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function RFCOMM_RemoveServer +** +** Description This function is called to close the server port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** +*******************************************************************************/ +int RFCOMM_RemoveServer (UINT16 handle) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("RFCOMM_RemoveServer() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_RemoveServer() BAD handle:%d", handle); + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + /* Do not report any events to the client any more. */ + p_port->p_mgmt_callback = NULL; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_RemoveServer() Not opened:%d", handle); + return (PORT_SUCCESS); + } + + /* this port will be deallocated after closing */ + p_port->keep_port_handle = FALSE; + p_port->state = PORT_STATE_CLOSING; + + port_start_close (p_port); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_SetEventCallback +** +** Description This function is called to provide an address of the +** function which will be called when one of the events +** specified in the mask occures. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when an event +** specified in the mask occures. +** +** +*******************************************************************************/ +int PORT_SetEventCallback (UINT16 port_handle, tPORT_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + RFCOMM_TRACE_API1 ("PORT_SetEventCallback() handle:%d", port_handle); + + p_port->p_callback = p_port_cb; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SetDataCallback +** +** Description This function is when a data packet is received +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when data packet +** is received. +** +** +*******************************************************************************/ +int PORT_SetDataCallback (UINT16 port_handle, tPORT_DATA_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SetDataCallback() handle:%d cb 0x%x", port_handle, p_port_cb); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_port->p_data_callback = p_port_cb; + + return (PORT_SUCCESS); +} +/******************************************************************************* +** +** Function PORT_SetCODataCallback +** +** Description This function is when a data packet is received +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_callback - address of the callback function which should +** be called from the RFCOMM when data packet +** is received. +** +** +*******************************************************************************/ +int PORT_SetDataCOCallback (UINT16 port_handle, tPORT_DATA_CO_CALLBACK *p_port_cb) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SetDataCOCallback() handle:%d cb 0x%x", port_handle, p_port_cb); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_port->p_data_co_callback = p_port_cb; + + return (PORT_SUCCESS); +} + + + +/******************************************************************************* +** +** Function PORT_SetEventMask +** +** Description This function is called to close the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** mask - Bitmask of the events the host is interested in +** +*******************************************************************************/ +int PORT_SetEventMask (UINT16 port_handle, UINT32 mask) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SetEventMask() handle:%d mask:0x%x", port_handle, mask); + + /* Check if handle is valid to avoid crashing */ + if ((port_handle == 0) || (port_handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[port_handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_port->ev_mask = mask; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_CheckConnection +** +** Description This function returns PORT_SUCCESS if connection referenced +** by handle is up and running +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** bd_addr - OUT bd_addr of the peer +** p_lcid - OUT L2CAP's LCID +** +*******************************************************************************/ +int PORT_CheckConnection (UINT16 handle, BD_ADDR bd_addr, UINT16 *p_lcid) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_CheckConnection() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->rfc.state != RFC_STATE_OPENED)) + { + return (PORT_LINE_ERR); + } + + memcpy (bd_addr, p_port->rfc.p_mcb->bd_addr, BD_ADDR_LEN); + if (p_lcid) + *p_lcid = p_port->rfc.p_mcb->lcid; + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_IsOpening +** +** Description This function returns TRUE if there is any RFCOMM connection +** opening in process. +** +** Parameters: TRUE if any connection opening is found +** bd_addr - bd_addr of the peer +** +*******************************************************************************/ +BOOLEAN PORT_IsOpening (BD_ADDR bd_addr) +{ + UINT8 xx, yy; + tRFC_MCB *p_mcb = NULL; + tPORT *p_port; + BOOLEAN found_port; + + /* Check for any rfc_mcb which is in the middle of opening. */ + for (xx = 0; xx < MAX_BD_CONNECTIONS; xx++) + { + if ((rfc_cb.port.rfc_mcb[xx].state > RFC_MX_STATE_IDLE) && + (rfc_cb.port.rfc_mcb[xx].state < RFC_MX_STATE_CONNECTED)) + { + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + + if (rfc_cb.port.rfc_mcb[xx].state == RFC_MX_STATE_CONNECTED) + { + found_port = FALSE; + p_mcb = &rfc_cb.port.rfc_mcb[xx]; + p_port = &rfc_cb.port.port[0]; + + for (yy = 0; yy < MAX_RFC_PORTS; yy++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + found_port = TRUE; + break; + } + } + + if ((!found_port) || + (found_port && (p_port->rfc.state < RFC_STATE_OPENED))) + { + /* Port is not established yet. */ + memcpy (bd_addr, rfc_cb.port.rfc_mcb[xx].bd_addr, BD_ADDR_LEN); + return TRUE; + } + } + } + + return FALSE; +} + +/******************************************************************************* +** +** Function PORT_SetState +** +** Description This function configures connection according to the +** specifications in the tPORT_STATE structure. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure containing +** configuration information for the connection. +** +** +*******************************************************************************/ +int PORT_SetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + UINT8 baud_rate; + + RFCOMM_TRACE_API1 ("PORT_SetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + RFCOMM_TRACE_API2 ("PORT_SetState() handle:%d FC_TYPE:0x%x", handle, + p_settings->fc_type); + + baud_rate = p_port->user_port_pars.baud_rate; + p_port->user_port_pars = *p_settings; + + /* for now we've been asked to pass only baud rate */ + if (baud_rate != p_settings->baud_rate) + { + port_start_par_neg (p_port); + } + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetRxQueueCnt +** +** Description This function return number of buffers on the rx queue. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_rx_queue_count - Pointer to return queue count in. +** +*******************************************************************************/ +int PORT_GetRxQueueCnt (UINT16 handle, UINT16 *p_rx_queue_count) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_GetRxQueueCnt() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + *p_rx_queue_count = p_port->rx.queue_size; + + RFCOMM_TRACE_API2 ("PORT_GetRxQueueCnt() p_rx_queue_count:%d, p_port->rx.queue.count = %d", + *p_rx_queue_count, p_port->rx.queue_size); + + return (PORT_SUCCESS); +} + +/******************************************************************************* +** +** Function PORT_GetState +** +** Description This function is called to fill tPORT_STATE structure +** with the curremt control settings for the port +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_settings - Pointer to a tPORT_STATE structure in which +** configuration information is returned. +** +*******************************************************************************/ +int PORT_GetState (UINT16 handle, tPORT_STATE *p_settings) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_GetState() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + *p_settings = p_port->user_port_pars; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Control +** +** Description This function directs a specified connection to pass control +** control information to the peer device. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** signal = specify the function to be passed +** +*******************************************************************************/ +int PORT_Control (UINT16 handle, UINT8 signal) +{ + tPORT *p_port; + UINT8 old_modem_signal; + + RFCOMM_TRACE_API2 ("PORT_Control() handle:%d signal:0x%x", handle, signal); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + old_modem_signal = p_port->local_ctrl.modem_signal; + p_port->local_ctrl.break_signal = 0; + + switch (signal) + { + case PORT_SET_CTSRTS: + p_port->local_ctrl.modem_signal |= PORT_CTSRTS_ON; + break; + + case PORT_CLR_CTSRTS: + p_port->local_ctrl.modem_signal &= ~PORT_CTSRTS_ON; + break; + + case PORT_SET_DTRDSR: + p_port->local_ctrl.modem_signal |= PORT_DTRDSR_ON; + break; + + case PORT_CLR_DTRDSR: + p_port->local_ctrl.modem_signal &= ~PORT_DTRDSR_ON; + break; + + case PORT_SET_RI: + p_port->local_ctrl.modem_signal |= PORT_RING_ON; + break; + + case PORT_CLR_RI: + p_port->local_ctrl.modem_signal &= ~PORT_RING_ON; + break; + + case PORT_SET_DCD: + p_port->local_ctrl.modem_signal |= PORT_DCD_ON; + break; + + case PORT_CLR_DCD: + p_port->local_ctrl.modem_signal &= ~PORT_DCD_ON; + break; + } + + if (signal == PORT_BREAK) + p_port->local_ctrl.break_signal = PORT_BREAK_DURATION; + else if (p_port->local_ctrl.modem_signal == old_modem_signal) + return (PORT_SUCCESS); + + port_start_control (p_port); + + RFCOMM_TRACE_EVENT4 ("PORT_Control DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->local_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_FlowControl +** +** Description This function directs a specified connection to pass +** flow control message to the peer device. Enable flag passed +** shows if port can accept more data. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** enable - enables data flow +** +*******************************************************************************/ +int PORT_FlowControl (UINT16 handle, BOOLEAN enable) +{ + tPORT *p_port; + BOOLEAN old_fc; + UINT32 events; + + RFCOMM_TRACE_API2 ("PORT_FlowControl() handle:%d enable: %d", handle, enable); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) + { + return (PORT_NOT_OPENED); + } + + p_port->rx.user_fc = !enable; + + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + if (!p_port->rx.user_fc) + { + port_flow_control_peer(p_port, TRUE, 0); + } + } + else + { + old_fc = p_port->local_ctrl.fc; + + /* FC is set if user is set or peer is set */ + p_port->local_ctrl.fc = (p_port->rx.user_fc | p_port->rx.peer_fc); + + if (p_port->local_ctrl.fc != old_fc) + port_start_control (p_port); + } + + /* Need to take care of the case when we could not deliver events */ + /* to the application because we were flow controlled */ + if (enable && (p_port->rx.queue_size != 0)) + { + events = PORT_EV_RXCHAR; + if (p_port->rx_flag_ev_pending) + { + p_port->rx_flag_ev_pending = FALSE; + events |= PORT_EV_RXFLAG; + } + + events &= p_port->ev_mask; + if (p_port->p_callback && events) + { + p_port->p_callback (events, p_port->inx); + } + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetModemStatus +** +** Description This function retrieves modem control signals. Normally +** application will call this function after a callback +** function is called with notification that one of signals +** has been changed. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_signal - specify the pointer to control signals info +** +*******************************************************************************/ +int PORT_GetModemStatus (UINT16 handle, UINT8 *p_signal) +{ + tPORT *p_port; + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + *p_signal = p_port->peer_ctrl.modem_signal; + + RFCOMM_TRACE_API2 ("PORT_GetModemStatus() handle:%d signal:%x", handle, *p_signal); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ClearError +** +** Description This function retreives information about a communications +** error and reports current status of a connection. The +** function should be called when an error occures to clear +** the connection error flag and to enable additional read +** and write operations. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_errors - pointer of the variable to receive error codes +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_ClearError (UINT16 handle, UINT16 *p_errors, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_ClearError() handle:%d", handle); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + *p_errors = p_port->line_status; + + /* This is the only call to clear error status. We can not clear */ + /* connection failed status. To clean it port should be closed and reopened */ + p_port->line_status = (p_port->line_status & LINE_STATUS_FAILED); + + PORT_GetQueueStatus (handle, p_status); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_SendError +** +** Description This function send a communications error to the peer device +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** errors - receive error codes +** +*******************************************************************************/ +int PORT_SendError (UINT16 handle, UINT8 errors) +{ + tPORT *p_port; + + RFCOMM_TRACE_API2 ("PORT_SendError() handle:%d errors:0x%x", handle, errors); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (!p_port->rfc.p_mcb) + { + return (PORT_NOT_OPENED); + } + + RFCOMM_LineStatusReq (p_port->rfc.p_mcb, p_port->dlci, errors); + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_GetQueueStatus +** +** Description This function reports current status of a connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_status - pointer to the tPORT_STATUS structur to receive +** connection status +** +*******************************************************************************/ +int PORT_GetQueueStatus (UINT16 handle, tPORT_STATUS *p_status) +{ + tPORT *p_port; + + /* RFCOMM_TRACE_API1 ("PORT_GetQueueStatus() handle:%d", handle); */ + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + p_status->in_queue_size = (UINT16) p_port->rx.queue_size; + p_status->out_queue_size = (UINT16) p_port->tx.queue_size; + + p_status->mtu_size = (UINT16) p_port->peer_mtu; + + p_status->flags = 0; + + if (!(p_port->peer_ctrl.modem_signal & PORT_CTSRTS_ON)) + p_status->flags |= PORT_FLAG_CTS_HOLD; + + if (!(p_port->peer_ctrl.modem_signal & PORT_DTRDSR_ON)) + p_status->flags |= PORT_FLAG_DSR_HOLD; + + if (!(p_port->peer_ctrl.modem_signal & PORT_DCD_ON)) + p_status->flags |= PORT_FLAG_RLSD_HOLD; + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Purge +** +** Description This function discards all the data from the output or +** input queues of the specified connection. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** purge_flags - specify the action to take. +** +*******************************************************************************/ +int PORT_Purge (UINT16 handle, UINT8 purge_flags) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + UINT32 events; + + RFCOMM_TRACE_API2 ("PORT_Purge() handle:%d flags:0x%x", handle, purge_flags); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (purge_flags & PORT_PURGE_RXCLEAR) + { + PORT_SCHEDULE_LOCK; /* to prevent missing credit */ + + count = p_port->rx.queue.count; + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->rx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + /* If we flowed controlled peer based on rx_queue size enable data again */ + if (count) + port_flow_control_peer (p_port, TRUE, count); + } + + if (purge_flags & PORT_PURGE_TXCLEAR) + { + PORT_SCHEDULE_LOCK; /* to prevent tx.queue_size from being negative */ + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->tx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + events = PORT_EV_TXEMPTY; + + events |= port_flow_control_user (p_port); + + events &= p_port->ev_mask; + + if ((p_port->p_callback != NULL) && events) + (p_port->p_callback)(events, p_port->inx); + } + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_ReadData +** +** Description Normally not GKI aware application will call this function +** after receiving PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_ReadData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT16 count; + + RFCOMM_TRACE_API2 ("PORT_ReadData() handle:%d max_len:%d", handle, max_len); + + /* Initialize this in case of an error */ + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + p_buf = (BT_HDR *)GKI_getfirst (&p_port->rx.queue); + if (!p_buf) + return (PORT_SUCCESS); + + count = 0; + + while (max_len && p_buf) + { + if (p_buf->len > max_len) + { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, max_len); + p_buf->offset += max_len; + p_buf->len -= max_len; + + *p_len += max_len; + + PORT_SCHEDULE_LOCK; + + p_port->rx.queue_size -= max_len; + + PORT_SCHEDULE_UNLOCK; + + break; + } + else + { + memcpy (p_data, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + *p_len += p_buf->len; + max_len -= p_buf->len; + + PORT_SCHEDULE_LOCK; + + p_port->rx.queue_size -= p_buf->len; + + if (max_len) + { + p_data += p_buf->len; + p_buf = (BT_HDR *)GKI_getnext (p_buf); + } + + GKI_freebuf (GKI_dequeue (&p_port->rx.queue)); + + PORT_SCHEDULE_UNLOCK; + + count++; + } + } + + if (*p_len == 1) + { + RFCOMM_TRACE_EVENT3 ("PORT_ReadData queue:%d returned:%d %x", p_port->rx.queue_size, *p_len, (p_data[0])); + } + else + { + RFCOMM_TRACE_EVENT2 ("PORT_ReadData queue:%d returned:%d", p_port->rx.queue_size, *p_len); + } + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, count); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Read +** +** Description Normally application will call this function after receiving +** PORT_EV_RXCHAR event. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Read (UINT16 handle, BT_HDR **pp_buf) +{ + tPORT *p_port; + BT_HDR *p_buf; + + RFCOMM_TRACE_API1 ("PORT_Read() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + return (PORT_LINE_ERR); + } + + PORT_SCHEDULE_LOCK; + + p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue); + if (p_buf) + { + p_port->rx.queue_size -= p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + port_flow_control_peer (p_port, TRUE, 1); + } + else + { + PORT_SCHEDULE_UNLOCK; + } + + *pp_buf = p_buf; + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: p_port - pointer to address of port control block +** p_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +static int port_write (tPORT *p_port, BT_HDR *p_buf) +{ + /* We should not allow to write data in to server port when connection is not opened */ + if (p_port->is_server && (p_port->rfc.state != RFC_STATE_OPENED)) + { + GKI_freebuf (p_buf); + return (PORT_CLOSED); + } + + /* Keep the data in pending queue if peer does not allow data, or */ + /* Peer is not ready or Port is not yet opened or initial port control */ + /* command has not been sent */ + if (p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->rfc.state != RFC_STATE_OPENED) + || ((p_port->port_ctrl & (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED)) != + (PORT_CTRL_REQ_SENT | PORT_CTRL_IND_RECEIVED))) + { + if ((p_port->tx.queue_size > PORT_TX_CRITICAL_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_CRITICAL_WM)) + { + RFCOMM_TRACE_WARNING1 ("PORT_Write: Queue size: %d", + p_port->tx.queue_size); + + GKI_freebuf (p_buf); + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & PORT_EV_ERR)) + p_port->p_callback (PORT_EV_ERR, p_port->inx); + + return (PORT_TX_FULL); + } + + RFCOMM_TRACE_EVENT4 ("PORT_Write : Data is enqued. flow disabled %d peer_ready %d state %d ctrl_state %x", + p_port->tx.peer_fc, + (p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready), + p_port->rfc.state, + p_port->port_ctrl); + + GKI_enqueue (&p_port->tx.queue, p_buf); + p_port->tx.queue_size += p_buf->len; + + return (PORT_CMD_PENDING); + } + else + { + RFCOMM_TRACE_EVENT0 ("PORT_Write : Data is being sent"); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + return (PORT_SUCCESS); + } +} + +/******************************************************************************* +** +** Function PORT_Write +** +** Description This function when a data packet is received from the apper +** layer task. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** pp_buf - pointer to address of buffer with data, +** +*******************************************************************************/ +int PORT_Write (UINT16 handle, BT_HDR *p_buf) +{ + tPORT *p_port; + UINT32 event = 0; + int rc; + + RFCOMM_TRACE_API1 ("PORT_Write() handle:%d", handle); + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + GKI_freebuf (p_buf); + return (PORT_BAD_HANDLE); + } + + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + GKI_freebuf (p_buf); + return (PORT_NOT_OPENED); + } + + if (p_port->line_status) + { + RFCOMM_TRACE_WARNING1 ("PORT_Write: Data dropped line_status:0x%x", + p_port->line_status); + GKI_freebuf (p_buf); + return (PORT_LINE_ERR); + } + + rc = port_write (p_port, p_buf); + event |= port_flow_control_user (p_port); + + switch (rc) + { + case PORT_TX_FULL: + event |= PORT_EV_ERR; + break; + + case PORT_SUCCESS: + event |= (PORT_EV_TXCHAR | PORT_EV_TXEMPTY); + break; + } + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) + (p_port->p_callback)(event, p_port->inx); + + return (PORT_SUCCESS); +} +/******************************************************************************* +** +** Function PORT_WriteDataCO +** +** Description Normally not GKI aware application will call this function +** to send data to the port by callout functions +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** fd - socket fd +** p_len - Byte count returned +** +*******************************************************************************/ +int PORT_WriteDataCO (UINT16 handle, int* p_len) +{ + + tPORT *p_port; + BT_HDR *p_buf; + UINT32 event = 0; + int rc = 0; + UINT16 length; + + RFCOMM_TRACE_API1 ("PORT_WriteDataCO() handle:%d", handle); + int written; + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_WARNING1 ("PORT_WriteDataByFd() no port state:%d", p_port->state); + return (PORT_NOT_OPENED); + } + + if (!p_port->peer_mtu) + { + RFCOMM_TRACE_ERROR1 ("PORT_WriteDataByFd() peer_mtu:%d", p_port->peer_mtu); + return (PORT_UNKNOWN_ERROR); + } + int available = 0; + //if(ioctl(fd, FIONREAD, &available) < 0) + if(p_port->p_data_co_callback(handle, (UINT8*)&available, sizeof(available), + DATA_CO_CALLBACK_TYPE_OUTGOING_SIZE) == FALSE) + { + RFCOMM_TRACE_ERROR1("p_data_co_callback DATA_CO_CALLBACK_TYPE_INCOMING_SIZE failed, available:%d", available); + return (PORT_UNKNOWN_ERROR); + } + /* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */ + length = RFCOMM_DATA_POOL_BUF_SIZE - + (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD); + + /* If there are buffers scheduled for transmission check if requested */ + /* data fits into the end of the queue */ + PORT_SCHEDULE_LOCK; + + if (((p_buf = (BT_HDR *)p_port->tx.queue.p_last) != NULL) + && (((int)p_buf->len + available) <= (int)p_port->peer_mtu) + && (((int)p_buf->len + available) <= (int)length)) + { + //if(recv(fd, (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, available, 0) != available) + if(p_port->p_data_co_callback(handle, (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, + available, DATA_CO_CALLBACK_TYPE_OUTGOING) == FALSE) + + { + error("p_data_co_callback DATA_CO_CALLBACK_TYPE_OUTGOING failed, available:%d", available); + return (PORT_UNKNOWN_ERROR); + } + //memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, p_data, max_len); + p_port->tx.queue_size += (UINT16)available; + + *p_len = available; + p_buf->len += (UINT16)available; + + PORT_SCHEDULE_UNLOCK; + + return (PORT_SUCCESS); + } + + PORT_SCHEDULE_UNLOCK; + + //int max_read = length < p_port->peer_mtu ? length : p_port->peer_mtu; + + //max_read = available < max_read ? available : max_read; + + while (available) + { + /* if we're over buffer high water mark, we're done */ + if ((p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_HIGH_WM)) + break; + + /* continue with rfcomm data write */ + p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_DATA_POOL_ID); + if (!p_buf) + break; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET; + p_buf->layer_specific = handle; + + if (p_port->peer_mtu < length) + length = p_port->peer_mtu; + if (available < (int)length) + length = (UINT16)available; + p_buf->len = length; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + //memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, length); + //if(recv(fd, (UINT8 *)(p_buf + 1) + p_buf->offset, (int)length, 0) != (int)length) + if(p_port->p_data_co_callback(handle, (UINT8 *)(p_buf + 1) + p_buf->offset, length, + DATA_CO_CALLBACK_TYPE_OUTGOING) == FALSE) + { + error("p_data_co_callback DATA_CO_CALLBACK_TYPE_OUTGOING failed, length:%d", length); + return (PORT_UNKNOWN_ERROR); + } + + + RFCOMM_TRACE_EVENT1 ("PORT_WriteData %d bytes", length); + + rc = port_write (p_port, p_buf); + + /* If queue went below the threashold need to send flow control */ + event |= port_flow_control_user (p_port); + + if (rc == PORT_SUCCESS) + event |= PORT_EV_TXCHAR; + + if ((rc != PORT_SUCCESS) && (rc != PORT_CMD_PENDING)) + break; + + *p_len += length; + available -= (int)length; + } + if (!available && (rc != PORT_CMD_PENDING) && (rc != PORT_TX_QUEUE_DISABLED)) + event |= PORT_EV_TXEMPTY; + + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) + (p_port->p_callback)(event, p_port->inx); + + return (PORT_SUCCESS); +} + + + +/******************************************************************************* +** +** Function PORT_WriteData +** +** Description Normally not GKI aware application will call this function +** to send data to the port. +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** p_len - Byte count received +** +*******************************************************************************/ +int PORT_WriteData (UINT16 handle, char *p_data, UINT16 max_len, UINT16 *p_len) +{ + tPORT *p_port; + BT_HDR *p_buf; + UINT32 event = 0; + int rc = 0; + UINT16 length; + + RFCOMM_TRACE_API1 ("PORT_WriteData() max_len:%d", max_len); + + *p_len = 0; + + /* Check if handle is valid to avoid crashing */ + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + RFCOMM_TRACE_WARNING1 ("PORT_WriteData() no port state:%d", p_port->state); + return (PORT_NOT_OPENED); + } + + if (!max_len || !p_port->peer_mtu) + { + RFCOMM_TRACE_ERROR1 ("PORT_WriteData() peer_mtu:%d", p_port->peer_mtu); + return (PORT_UNKNOWN_ERROR); + } + + /* Length for each buffer is the smaller of GKI buffer, peer MTU, or max_len */ + length = RFCOMM_DATA_POOL_BUF_SIZE - + (UINT16)(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + RFCOMM_DATA_OVERHEAD); + + /* If there are buffers scheduled for transmission check if requested */ + /* data fits into the end of the queue */ + PORT_SCHEDULE_LOCK; + + if (((p_buf = (BT_HDR *)p_port->tx.queue.p_last) != NULL) + && ((p_buf->len + max_len) <= p_port->peer_mtu) + && ((p_buf->len + max_len) <= length)) + { + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len, p_data, max_len); + p_port->tx.queue_size += max_len; + + *p_len = max_len; + p_buf->len += max_len; + + PORT_SCHEDULE_UNLOCK; + + return (PORT_SUCCESS); + } + + PORT_SCHEDULE_UNLOCK; + + while (max_len) + { + /* if we're over buffer high water mark, we're done */ + if ((p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_HIGH_WM)) + break; + + /* continue with rfcomm data write */ + p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_DATA_POOL_ID); + if (!p_buf) + break; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET; + p_buf->layer_specific = handle; + + if (p_port->peer_mtu < length) + length = p_port->peer_mtu; + if (max_len < length) + length = max_len; + p_buf->len = length; + p_buf->event = BT_EVT_TO_BTU_SP_DATA; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, length); + + RFCOMM_TRACE_EVENT1 ("PORT_WriteData %d bytes", length); + + rc = port_write (p_port, p_buf); + + /* If queue went below the threashold need to send flow control */ + event |= port_flow_control_user (p_port); + + if (rc == PORT_SUCCESS) + event |= PORT_EV_TXCHAR; + + if ((rc != PORT_SUCCESS) && (rc != PORT_CMD_PENDING)) + break; + + *p_len += length; + max_len -= length; + p_data += length; + + } + if (!max_len && (rc != PORT_CMD_PENDING) && (rc != PORT_TX_QUEUE_DISABLED)) + event |= PORT_EV_TXEMPTY; + + /* Mask out all events that are not of interest to user */ + event &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && event) + (p_port->p_callback)(event, p_port->inx); + + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function PORT_Test +** +** Description Application can call this function to send RFCOMM Test frame +** +** Parameters: handle - Handle returned in the RFCOMM_CreateConnection +** p_data - Data area +** max_len - Byte count requested +** +*******************************************************************************/ +int PORT_Test (UINT16 handle, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_buf; + tPORT *p_port; + + RFCOMM_TRACE_API1 ("PORT_Test() len:%d", len); + + if ((handle == 0) || (handle > MAX_RFC_PORTS)) + { + return (PORT_BAD_HANDLE); + } + p_port = &rfc_cb.port.port[handle - 1]; + + if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) + { + return (PORT_NOT_OPENED); + } + + if (len > ((p_port->mtu == 0) ? RFCOMM_DEFAULT_MTU : p_port->mtu)) + { + return (PORT_UNKNOWN_ERROR); + } + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) != NULL) + { + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + p_buf->len = len; + + memcpy ((UINT8 *)(p_buf + 1) + p_buf->offset, p_data, p_buf->len); + + rfc_send_test (p_port->rfc.p_mcb, TRUE, p_buf); + return (PORT_SUCCESS); + } + else + { + return (PORT_NO_MEM); + } +} + +/******************************************************************************* +** +** Function RFCOMM_Init +** +** Description This function is called to initialize RFCOMM layer +** +*******************************************************************************/ +void RFCOMM_Init (void) +{ + memset (&rfc_cb, 0, sizeof (tRFC_CB)); /* Init RFCOMM control block */ + + rfc_cb.rfc.last_mux = MAX_BD_CONNECTIONS; + +#if defined(RFCOMM_INITIAL_TRACE_LEVEL) + rfc_cb.trace_level = RFCOMM_INITIAL_TRACE_LEVEL; +#else + rfc_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + rfcomm_l2cap_if_init (); +} + +/******************************************************************************* +** +** Function PORT_SetTraceLevel +** +** Description This function sets the trace level for RFCOMM. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 PORT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + rfc_cb.trace_level = new_level; + + return (rfc_cb.trace_level); +} + diff --git a/stack/rfcomm/port_int.h b/stack/rfcomm/port_int.h new file mode 100644 index 0000000..2313ace --- /dev/null +++ b/stack/rfcomm/port_int.h @@ -0,0 +1,252 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains definitions internal to the PORT unit + * + *****************************************************************************/ + +#ifndef PORT_INT_H +#define PORT_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" + +/* Local events passed when application event is sent from the api to PORT */ +/* ???*/ +#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT) +#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT) + +/* +** Flow control configuration values for the mux +*/ +#define PORT_FC_UNDEFINED 0 /* mux flow control mechanism not defined yet */ +#define PORT_FC_TS710 1 /* use TS 07.10 flow control */ +#define PORT_FC_CREDIT 2 /* use RFCOMM credit based flow control */ + +/* +** Define Port Data Transfere control block +*/ +typedef struct +{ + BUFFER_Q queue; /* Queue of buffers waiting to be sent */ + BOOLEAN peer_fc; /* TRUE if flow control is set based on peer's request */ + BOOLEAN user_fc; /* TRUE if flow control is set based on user's request */ + UINT32 queue_size; /* Number of data bytes in the queue */ + tPORT_CALLBACK *p_callback; /* Address of the callback function */ +} tPORT_DATA; + +/* +** Port control structure used to pass modem info +*/ +typedef struct +{ +#define MODEM_SIGNAL_DTRDSR 0x01 +#define MODEM_SIGNAL_RTSCTS 0x02 +#define MODEM_SIGNAL_RI 0x04 +#define MODEM_SIGNAL_DCD 0x08 + + UINT8 modem_signal; /* [DTR/DSR | RTS/CTS | RI | DCD ] */ + + UINT8 break_signal; /* 0-3 s in steps of 200 ms */ + + UINT8 discard_buffers; /* 0 - do not discard, 1 - discard */ + +#define RFCOMM_CTRL_BREAK_ASAP 0 +#define RFCOMM_CTRL_BREAK_IN_SEQ 1 + + UINT8 break_signal_seq; /* as soon as possible | in sequence (default) */ + + BOOLEAN fc; /* TRUE when the device is unable to accept frames */ +} tPORT_CTRL; + + +/* +** RFCOMM multiplexer Control Block +*/ +typedef struct +{ + TIMER_LIST_ENT tle; /* Timer list entry */ + BUFFER_Q cmd_q; /* Queue for command messages on this mux */ + UINT8 port_inx[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */ + /* tPORT based on dlci */ + BD_ADDR bd_addr; /* BD ADDR of the peer if initiator */ + UINT16 lcid; /* Local cid used for this channel */ + UINT16 peer_l2cap_mtu; /* Max frame that can be sent to peer L2CAP */ + UINT8 state; /* Current multiplexer channel state */ + UINT8 is_initiator; /* TRUE if this side sends SABME (dlci=0) */ + BOOLEAN local_cfg_sent; + BOOLEAN peer_cfg_rcvd; + BOOLEAN restart_required; /* TRUE if has to restart channel after disc */ + BOOLEAN peer_ready; /* True if other side can accept frames */ + UINT8 flow; /* flow control mechanism for this mux */ + BOOLEAN l2cap_congested; /* TRUE if L2CAP is congested */ + BOOLEAN is_disc_initiator; /* TRUE if initiated disc of port */ + UINT16 pending_lcid; /* store LCID for incoming connection while connecting */ + UINT8 pending_id; /* store l2cap ID for incoming connection while connecting */ +} tRFC_MCB; + + +/* +** RFCOMM Port Connection Control Block +*/ +struct t_rfc_port +{ +#define RFC_PORT_STATE_IDLE 0 +#define RFC_PORT_STATE_WAIT_START 1 +#define RFC_PORT_STATE_OPENING 2 +#define RFC_PORT_STATE_OPENED 3 +#define RFC_PORT_STATE_CLOSING 4 + + UINT8 state; /* Current state of the connection */ + +#define RFC_RSP_PN 0x01 +#define RFC_RSP_RPN_REPLY 0x02 +#define RFC_RSP_RPN 0x04 +#define RFC_RSP_MSC 0x08 +#define RFC_RSP_RLS 0x10 + + UINT8 expected_rsp; + + tRFC_MCB *p_mcb; + + TIMER_LIST_ENT tle; /* Timer list entry */ +}; +typedef struct t_rfc_port tRFC_PORT; + + +/* +** Define control block containing information about PORT connection +*/ +struct t_port_info +{ + UINT8 inx; /* Index of this control block in the port_info array */ + BOOLEAN in_use; /* True when structure is allocated */ + +#define PORT_STATE_CLOSED 0 +#define PORT_STATE_OPENING 1 +#define PORT_STATE_OPENED 2 +#define PORT_STATE_CLOSING 3 + + UINT8 state; /* State of the application */ + + UINT8 scn; /* Service channel number */ + UINT16 uuid; /* Service UUID */ + + BD_ADDR bd_addr; /* BD ADDR of the device for the multiplexer channel */ + BOOLEAN is_server; /* TRUE if the server application */ + UINT8 dlci; /* DLCI of the connection */ + + UINT8 error; /* Last error detected */ + + UINT8 line_status; /* Line status as reported by peer */ + + UINT8 default_signal_state; /* Initial signal state depending on uuid */ + + UINT16 mtu; /* Max MTU that port can receive */ + UINT16 peer_mtu; /* Max MTU that port can send */ + + tPORT_DATA tx; /* Control block for data from app to peer */ + tPORT_DATA rx; /* Control block for data from peer to app */ + + tPORT_STATE user_port_pars; /* Port parameters for user connection */ + tPORT_STATE peer_port_pars; /* Port parameters for user connection */ + + tPORT_CTRL local_ctrl; + tPORT_CTRL peer_ctrl; + +#define PORT_CTRL_REQ_SENT 0x01 +#define PORT_CTRL_REQ_CONFIRMED 0x02 +#define PORT_CTRL_IND_RECEIVED 0x04 +#define PORT_CTRL_IND_RESPONDED 0x08 + + UINT8 port_ctrl; /* Modem Status Command */ + + BOOLEAN rx_flag_ev_pending; /* RXFLAG Character is received */ + + tRFC_PORT rfc; /* RFCOMM port control block */ + + UINT32 ev_mask; /* Event mask for the callback */ + tPORT_CALLBACK *p_callback; /* Pointer to users callback function */ + tPORT_CALLBACK *p_mgmt_callback; /* Callback function to receive connection up/down */ + tPORT_DATA_CALLBACK *p_data_callback; /* Callback function to receive data indications */ + tPORT_DATA_CO_CALLBACK *p_data_co_callback; /* Callback function with callouts and flowctrl */ + UINT16 credit_tx; /* Flow control credits for tx path */ + UINT16 credit_rx; /* Flow control credits for rx path, this is */ + /* number of buffers peer is allowed to sent */ + UINT16 credit_rx_max; /* Max number of credits we will allow this guy to sent */ + UINT16 credit_rx_low; /* Number of credits when we send credit update */ + UINT16 rx_buf_critical; /* port receive queue critical watermark level */ + BOOLEAN keep_port_handle; /* TRUE if port is not deallocated when closing */ + /* it is set to TRUE for server when allocating port */ + UINT16 keep_mtu; /* Max MTU that port can receive by server */ +}; +typedef struct t_port_info tPORT; + + +/* Define the PORT/RFCOMM control structure +*/ +typedef struct +{ + tPORT port[MAX_RFC_PORTS]; /* Port info pool */ + tRFC_MCB rfc_mcb[MAX_BD_CONNECTIONS]; /* RFCOMM bd_connections pool */ +} tPORT_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Functions provided by the port_utils.c +*/ +extern tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr); +extern void port_set_defaults (tPORT *p_port); +extern void port_select_mtu (tPORT *p_port); +extern void port_release_port (tPORT *p_port); +extern tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci); +extern tRFC_MCB *port_find_mcb (BD_ADDR bd_addr); +extern tPORT *port_find_dlci_port (UINT8 dlci); +extern tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr); +extern UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal); +extern UINT32 port_flow_control_user (tPORT *p_port); +extern void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count); + +/* +** Functions provided by the port_rfc.c +*/ +extern int port_open_continue (tPORT *p_port); +extern void port_start_port_open (tPORT *p_port); +extern void port_start_par_neg (tPORT *p_port); +extern void port_start_control (tPORT *p_port); +extern void port_start_close (tPORT *p_port); +extern void port_rfc_closed (tPORT *p_port, UINT8 res); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/rfcomm/port_rfc.c b/stack/rfcomm/port_rfc.c new file mode 100644 index 0000000..8980330 --- /dev/null +++ b/stack/rfcomm/port_rfc.c @@ -0,0 +1,1112 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains functions for port emulation entity and RFCOMM + * communications + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "btm_int.h" +#include "btm_api.h" +#include "port_int.h" +#include "rfc_int.h" + +/* +** Local function definitions +*/ +UINT32 port_rfc_send_tx_data (tPORT *p_port); +void port_rfc_closed (tPORT *p_port, UINT8 res); +void port_get_credits (tPORT *p_port, UINT8 k); + + +/******************************************************************************* +** +** Function port_open_continue +** +** Description This function is called after security manager completes +** required security checks. +** +** Returns void +** +*******************************************************************************/ +int port_open_continue (tPORT *p_port) +{ + tRFC_MCB *p_mcb; + + RFCOMM_TRACE_EVENT0 ("port_open_continue"); + + /* Check if multiplexer channel has already been established */ + if ((p_mcb = rfc_alloc_multiplexer_channel (p_port->bd_addr, TRUE)) == NULL) + { + RFCOMM_TRACE_WARNING0 ("port_open_continue no mx channel"); + port_release_port (p_port); + return (PORT_NO_RESOURCES); + } + + p_port->rfc.p_mcb = p_mcb; + + p_mcb->port_inx[p_port->dlci] = p_port->inx; + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + if (p_mcb->state == RFC_MX_STATE_CONNECTED) + { + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + } + else if ((p_mcb->state == RFC_MX_STATE_IDLE) + ||(p_mcb->state == RFC_MX_STATE_DISC_WAIT_UA)) + { + /* In RFC_MX_STATE_IDLE state, MX state machine will create connection */ + /* In RFC_MX_STATE_DISC_WAIT_UA state, MX state machine will recreate connection */ + /* after disconnecting is completed */ + RFCOMM_StartReq (p_mcb); + } + else + { + /* MX state machine ignores RFC_MX_EVENT_START_REQ in these states */ + /* When it enters RFC_MX_STATE_CONNECTED, it will check any openning ports */ + RFCOMM_TRACE_DEBUG1 ("port_open_continue: mx state(%d) mx channel is openning", p_mcb->state); + } + return (PORT_SUCCESS); +} + + +/******************************************************************************* +** +** Function port_start_control +** +** Description This function is called in the BTU_TASK context to +** send control information +** +** Returns void +** +*******************************************************************************/ +void port_start_control (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) + return; + + RFCOMM_ControlReq (p_mcb, p_port->dlci, &p_port->local_ctrl); +} + + +/******************************************************************************* +** +** Function port_start_par_neg +** +** Description This function is called in the BTU_TASK context to +** send configuration information +** +** Returns void +** +*******************************************************************************/ +void port_start_par_neg (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if (p_mcb == NULL) + return; + + RFCOMM_PortNegReq (p_mcb, p_port->dlci, &p_port->user_port_pars); +} + + +/******************************************************************************* +** +** Function port_start_close +** +** Description This function is called in the BTU_TASK context to +** release DLC +** +** Returns void +** +*******************************************************************************/ +void port_start_close (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + UINT8 old_signals; + UINT32 events = 0; + + /* At first indicate to the user that signals on the connection were dropped */ + p_port->line_status |= LINE_STATUS_FAILED; + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if(p_port->ev_mask & PORT_EV_CONNECT_ERR) + events |= PORT_EV_CONNECT_ERR; + + if(p_port->ev_mask & PORT_EV_ERR) + events |= PORT_EV_ERR; + + if ((p_port->p_callback != NULL) && events) + p_port->p_callback (events, p_port->inx); + + + /* Check if RFCOMM side has been closed while the message was queued */ + if ((p_mcb == NULL) || (p_port->rfc.state == RFC_STATE_CLOSED)) + { + /* Call management callback function before calling port_release_port() to clear tPort */ + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_CLOSED, p_port->inx); + + port_release_port (p_port); + } + else + { + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + } +} + + +/******************************************************************************* +** +** Function PORT_StartCnf +** +** Description This function is called from the RFCOMM layer when +** establishing of the multiplexer channel is completed. +** Continue establishing of the connection for all ports that +** are in the OPENING state +** +*******************************************************************************/ +void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result) +{ + tPORT *p_port; + int i; + BOOLEAN no_ports_up = TRUE; + + RFCOMM_TRACE_EVENT1 ("PORT_StartCnf result:%d", result); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + no_ports_up = FALSE; + + if (result == RFCOMM_SUCCESS) + RFCOMM_ParNegReq (p_mcb, p_port->dlci, p_port->mtu); + else + { + RFCOMM_TRACE_WARNING1 ("PORT_StartCnf failed result:%d", result); + + /* Warning: result is also set to 4 when l2cap connection + fails due to l2cap connect cnf (no_resources) */ + if( result == HCI_ERR_PAGE_TIMEOUT ) + p_port->error = PORT_PAGE_TIMEOUT; + else + p_port->error = PORT_START_FAILED; + + rfc_release_multiplexer_channel (p_mcb); + p_port->rfc.p_mcb = NULL; + + /* Send event to the application */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECT_ERR)) + (p_port->p_callback)(PORT_EV_CONNECT_ERR, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_START_FAILED, p_port->inx); + + port_release_port (p_port); + } + } + } + + /* There can be a situation when after starting connection, user closes the */ + /* port, we can catch it here to close multiplexor channel */ + if (no_ports_up) + { + rfc_check_mcb_active (p_mcb); + } +} + + +/******************************************************************************* +** +** Function PORT_StartInd +** +** Description This function is called from the RFCOMM layer when +** some peer device wants to establish a multiplexer +** connection. Check if there are any ports open with this +** or not assigned multiplexer. +** +*******************************************************************************/ +void PORT_StartInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("PORT_StartInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if ((p_port->rfc.p_mcb == NULL) + || (p_port->rfc.p_mcb == p_mcb)) + { + RFCOMM_StartRsp (p_mcb, RFCOMM_SUCCESS); + return; + } + } + RFCOMM_StartRsp (p_mcb, RFCOMM_ERROR); +} + + +/******************************************************************************* +** +** Function PORT_ParNegInd +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** If can not find the port do not accept the request. +** Otherwise save the MTU size supported by the peer. +** +*******************************************************************************/ +void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 our_cl; + UINT8 our_k; + + RFCOMM_TRACE_EVENT2 ("PORT_ParNegInd dlci:%d mtu:%d", dlci, mtu); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + /* If the port cannot be opened, send a DM. Per Errata 1205 */ + rfc_send_dm(p_mcb, dlci, FALSE); + /* check if this is the last port open, some headsets have + problem, they don't disconnect if we send DM */ + rfc_check_mcb_active( p_mcb ); + RFCOMM_TRACE_EVENT0( "PORT_ParNegInd: port not found" ); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + memcpy (p_port->bd_addr, p_mcb->bd_addr, BD_ADDR_LEN); + + /* Connection is up and we know local and remote features, select MTU */ + port_select_mtu (p_port); + + p_port->rfc.p_mcb = p_mcb; + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, set it now. If either we or peer wants TS 07.10, */ + /* use that. Otherwise both must want credit based, so use that. If flow is */ + /* already defined for this mux, we respond with that value. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) + { + if ((PORT_FC_DEFAULT == PORT_FC_TS710) || (cl == RFCOMM_PN_CONV_LAYER_TYPE_1)) + { + p_mcb->flow = PORT_FC_TS710; + } + else + { + p_mcb->flow = PORT_FC_CREDIT; + } + } + + /* Regardless of our flow control mechanism, if the PN cl is zero, we must */ + /* respond with zero. "A responding implementation must set this field to 14 */ + /* if (and only if) the PN request was 15." This could happen if a PN is sent */ + /* after the DLCI is already established-- the PN in that case must have cl = 0. */ + /* See RFCOMM spec 5.5.3 */ + if (cl == RFCOMM_PN_CONV_LAYER_TYPE_1) + { + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } + else if (p_mcb->flow == PORT_FC_CREDIT) + { + /* get credits */ + port_get_credits (p_port, k); + + /* Set convergence layer and number of credits (k) */ + our_cl = RFCOMM_PN_CONV_LAYER_CBFC_R; + our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = our_k; + } + else + { + /* must not be using credit based flow control; use TS 7.10 */ + our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + our_k = 0; + } + RFCOMM_ParNegRsp (p_mcb, dlci, p_port->mtu, our_cl, our_k); +} + + +/******************************************************************************* +** +** Function PORT_ParNegCnf +** +** Description This function is called from the RFCOMM layer to change +** DLCI parameters (currently only MTU is negotiated). +** Save the MTU size supported by the peer. +** If the confirmation is received during the port opening +** procedure send EstablishRequest to continue. +** +*******************************************************************************/ +void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT4 ("PORT_ParNegCnf dlci:%d mtu:%d cl: %d k: %d", dlci, mtu, cl, k); + + if (!p_port) + return; + + /* Flow control mechanism not set yet. Negotiate flow control mechanism. */ + if (p_mcb->flow == PORT_FC_UNDEFINED) + { + /* Our stack is configured for TS07.10 and they responded with credit-based. */ + /* This is illegal-- negotiation fails. */ + if ((PORT_FC_DEFAULT == PORT_FC_TS710) && (cl == RFCOMM_PN_CONV_LAYER_CBFC_R)) + { + rfc_send_disc (p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + } + /* Our stack is configured for credit-based and they responded with credit-based. */ + else if (cl == RFCOMM_PN_CONV_LAYER_CBFC_R) + { + p_mcb->flow = PORT_FC_CREDIT; + } + /* They responded with any other value. Treat this as negotiation to TS07.10. */ + else + { + p_mcb->flow = PORT_FC_TS710; + } + } + /* If mux flow control mechanism set, we honor that setting regardless of */ + /* the CL value in their response. This allows us to gracefully accept any */ + /* illegal PN negotiation scenarios. */ + + p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; + p_port->peer_mtu = p_port->mtu; + + if (p_mcb->flow == PORT_FC_CREDIT) + { + port_get_credits (p_port, k); + } + + if (p_port->state == PORT_STATE_OPENING) + RFCOMM_DlcEstablishReq (p_mcb, p_port->dlci, p_port->mtu); +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to establish a new DLC. If this is not the +** first message in the establishment procedure port_handle +** has a handle to the port control block otherwise the control +** block should be found based on the muliplexer channel and +** dlci. The block should be allocated allocated before +** meaning that application already made open. +** +*******************************************************************************/ +void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT2 ("PORT_DlcEstablishInd dlci:%d mtu:%d", dlci, mtu); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + RFCOMM_DlcEstablishRsp (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) + p_port->peer_mtu = mtu; + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + RFCOMM_DlcEstablishRsp (p_mcb, dlci, p_port->mtu, RFCOMM_SUCCESS); + + /* This is the server side. If application wants to know when connection */ + /* is established, thats the place */ + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx); + + p_port->state = PORT_STATE_OPENED; +} + + +/******************************************************************************* +** +** Function PORT_DlcEstablishCnf +** +** Description This function is called from the RFCOMM layer when peer +** acknowledges establish procedure (SABME/UA). Send reply +** to the user and set state to OPENED if result was +** successfull. +** +*******************************************************************************/ +void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT3 ("PORT_DlcEstablishCnf dlci:%d mtu:%d result:%d", dlci, mtu, result); + + if (!p_port) + return; + + if (result != RFCOMM_SUCCESS) + { + p_port->error = PORT_START_FAILED; + port_rfc_closed (p_port, PORT_START_FAILED); + return; + } + + /* If L2CAP's mtu less then RFCOMM's take it */ + if (mtu && (mtu < p_port->peer_mtu)) + p_port->peer_mtu = mtu; + + /* If there was an inactivity timer running for MCB stop it */ + rfc_timer_stop (p_mcb); + + if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED)) + (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (PORT_SUCCESS, p_port->inx); + + p_port->state = PORT_STATE_OPENED; + + /* RPN is required only if we want to tell DTE how the port should be opened */ + if ((p_port->uuid == UUID_SERVCLASS_DIALUP_NETWORKING) + || (p_port->uuid == UUID_SERVCLASS_FAX)) + RFCOMM_PortNegReq (p_port->rfc.p_mcb, p_port->dlci, NULL); + else + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); +} + + +/******************************************************************************* +** +** Function PORT_PortNegInd +** +** Description This function is called from the RFCOMM layer when peer +** device wants to set parameters of the port. As per the spec +** this message has to be sent before the first data packet +** and can be sent before establish. The block should be +** allocated before meaning that application already made open. +** +*******************************************************************************/ +void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_PortNegInd"); + + if (!p_port) + { + /* This can be a first request for this port */ + p_port = port_find_dlci_port (dlci); + if (!p_port) + { + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, 0); + return; + } + p_mcb->port_inx[dlci] = p_port->inx; + } + + /* Check if the flow control is acceptable on local side */ + p_port->peer_port_pars = *p_pars; + RFCOMM_PortNegRsp (p_mcb, dlci, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function PORT_PortNegCnf +** +** Description This function is called from the RFCOMM layer to change +** state for the port. Propagate change to the user. +** +*******************************************************************************/ +void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_PortNegCnf"); + + if (!p_port) + { + RFCOMM_TRACE_WARNING0 ("PORT_PortNegCnf no port"); + return; + } + /* Port negotiation failed. Drop the connection */ + if (result != RFCOMM_SUCCESS) + { + p_port->error = PORT_PORT_NEG_FAILED; + + RFCOMM_DlcReleaseReq (p_mcb, p_port->dlci); + + port_rfc_closed (p_port, PORT_PORT_NEG_FAILED); + return; + } + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) + { + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + } + else + { + RFCOMM_TRACE_WARNING0 ("PORT_PortNegCnf Control Already sent"); + } +} + + +/******************************************************************************* +** +** Function PORT_ControlInd +** +** Description This function is called from the RFCOMM layer on the modem +** signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event; + UINT8 old_signals; + + RFCOMM_TRACE_EVENT0 ("PORT_ControlInd"); + + if (!p_port) + return; + + old_signals = p_port->peer_ctrl.modem_signal; + + event = port_get_signal_changes (p_port, old_signals, p_pars->modem_signal); + + p_port->peer_ctrl = *p_pars; + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) + RFCOMM_ControlReq (p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl); + else + { + /* If this is the first time we received control RFCOMM is connected */ + if (!(p_port->port_ctrl & PORT_CTRL_IND_RECEIVED)) + { + event |= (PORT_EV_CONNECTED & p_port->ev_mask); + } + + if (p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED) + { + event |= port_rfc_send_tx_data(p_port); + } + } + + p_port->port_ctrl |= (PORT_CTRL_IND_RECEIVED | PORT_CTRL_IND_RESPONDED); + + if (p_pars->break_signal) + event |= (PORT_EV_BREAK & p_port->ev_mask); + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) + (p_port->p_callback)(event, p_port->inx); + + RFCOMM_TRACE_EVENT4 ("PORT_ControlInd DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d", + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DTRDSR) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RTSCTS) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_RI) ? 1 : 0), + ((p_port->peer_ctrl.modem_signal & MODEM_SIGNAL_DCD) ? 1 : 0)); + +} + + +/******************************************************************************* +** +** Function PORT_ControlCnf +** +** Description This function is called from the RFCOMM layer when +** peer acknowleges change of the modem signals. +** +*******************************************************************************/ +void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + + RFCOMM_TRACE_EVENT0 ("PORT_ControlCnf"); + + if (!p_port) + return; + + if (!(p_port->port_ctrl & PORT_CTRL_REQ_CONFIRMED)) + { + p_port->port_ctrl |= PORT_CTRL_REQ_CONFIRMED; + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) + event = (p_port->ev_mask & PORT_EV_CONNECTED); + } + + if (p_port->port_ctrl & PORT_CTRL_IND_RECEIVED) + { + event |= port_rfc_send_tx_data(p_port); + } + + /* execute call back function only if the application is registered for events */ + if (event && p_port->p_callback) + (p_port->p_callback)(event, p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_LineStatusInd +** +** Description This function is called from the RFCOMM layer when +** peer indicates change in the line status +** +*******************************************************************************/ +void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT32 event = 0; + + RFCOMM_TRACE_EVENT0 ("PORT_LineStatusInd"); + + if (!p_port) + return; + + p_port->line_status |= line_status; + + if (line_status & PORT_ERR_OVERRUN) + event |= PORT_EV_OVERRUN; + + if (line_status & PORT_ERR_BREAK) + event |= PORT_EV_BREAK; + + if (line_status & ~(PORT_ERR_OVERRUN | PORT_ERR_BREAK)) + event |= PORT_EV_ERR; + + if ((p_port->p_callback != NULL) && (p_port->ev_mask & event)) + p_port->p_callback ((p_port->ev_mask & event), p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_DlcReleaseInd +** +** Description This function is called from the RFCOMM layer when +** DLC connection is released. +** +*******************************************************************************/ +void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + RFCOMM_TRACE_EVENT0 ("PORT_DlcReleaseInd"); + + if (!p_port) + return; + + port_rfc_closed (p_port, PORT_CLOSED); +} + + +/******************************************************************************* +** +** Function PORT_CloseInd +** +** Description This function is called from the RFCOMM layer when +** multiplexer connection is released. +** +*******************************************************************************/ +void PORT_CloseInd (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("PORT_CloseInd"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + port_rfc_closed (p_port, PORT_PEER_CONNECTION_FAILED); + } + } + rfc_release_multiplexer_channel (p_mcb); +} + +/******************************************************************************* +** +** Function Port_TimeOutCloseMux +** +** Description This function is called when RFCOMM timesout on a command +** as a result multiplexer connection is closed. +** +*******************************************************************************/ +void Port_TimeOutCloseMux (tRFC_MCB *p_mcb) +{ + tPORT *p_port; + int i; + + RFCOMM_TRACE_EVENT0 ("Port_TimeOutCloseMux"); + + p_port = &rfc_cb.port.port[0]; + for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) + { + if (p_port->rfc.p_mcb == p_mcb) + { + port_rfc_closed (p_port, PORT_PEER_TIMEOUT); + } + } +} + + +/******************************************************************************* +** +** Function PORT_DataInd +** +** Description This function is called from the RFCOMM layer when data +** buffer is received from the peer. +** +*******************************************************************************/ +void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 rx_char1; + UINT32 events = 0; + UINT8 *p; + int i; + + RFCOMM_TRACE_EVENT1 ("PORT_DataInd with data length %d", p_buf->len); + if (!p_port) + { + GKI_freebuf (p_buf); + return; + } + /* If client registered callout callback with flow control we can just deliver receive data */ + if (p_port->p_data_co_callback) + { + /* Another packet is delivered to user. Send credits to peer if required */ + + if(p_port->p_data_co_callback(p_port->inx, (UINT8*)p_buf, -1, DATA_CO_CALLBACK_TYPE_INCOMING)) + port_flow_control_peer(p_port, TRUE, 1); + else port_flow_control_peer(p_port, FALSE, 0); + //GKI_freebuf (p_buf); + return; + } + + /* If client registered callback we can just deliver receive data */ + if (p_port->p_data_callback) + { + /* Another packet is delivered to user. Send credits to peer if required */ + port_flow_control_peer(p_port, TRUE, 1); + + p_port->p_data_callback (p_port->inx, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + GKI_freebuf (p_buf); + return; + } + + /* Check if rx queue exceeds the limit */ + if ((p_port->rx.queue_size + p_buf->len > PORT_RX_CRITICAL_WM) + || (p_port->rx.queue.count + 1 > p_port->rx_buf_critical)) + { + RFCOMM_TRACE_EVENT0 ("PORT_DataInd. Buffer over run. Dropping the buffer"); + GKI_freebuf (p_buf); + + RFCOMM_LineStatusReq (p_mcb, dlci, LINE_STATUS_OVERRUN); + return; + } + + /* If user registered to receive notification when a particular byte is */ + /* received we mast check all received bytes */ + if (((rx_char1 = p_port->user_port_pars.rx_char1) != 0) + && (p_port->ev_mask & PORT_EV_RXFLAG)) + { + for (i = 0, p = (UINT8 *)(p_buf + 1) + p_buf->offset; i < p_buf->len; i++) + { + if (*p++ == rx_char1) + { + events |= PORT_EV_RXFLAG; + break; + } + } + } + + PORT_SCHEDULE_LOCK; + + GKI_enqueue (&p_port->rx.queue, p_buf); + p_port->rx.queue_size += p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + /* perform flow control procedures if necessary */ + port_flow_control_peer(p_port, FALSE, 0); + + /* If user indicated flow control can not deliver any notifications to him */ + if (p_port->rx.user_fc) + { + if (events & PORT_EV_RXFLAG) + p_port->rx_flag_ev_pending = TRUE; + + return; + } + + events |= PORT_EV_RXCHAR; + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + if (p_port->p_callback && events) + p_port->p_callback (events, p_port->inx); +} + + +/******************************************************************************* +** +** Function PORT_FlowInd +** +** Description This function is called from the RFCOMM layer on the flow +** control signal change. Propagate change to the user. +** +*******************************************************************************/ +void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN enable_data) +{ + tPORT *p_port = (tPORT *)NULL; + UINT32 events = 0; + int i; + + RFCOMM_TRACE_EVENT1 ("PORT_FlowInd fc:%d", enable_data); + + if (dlci == 0) + { + p_mcb->peer_ready = enable_data; + } + else + { + if ((p_port = port_find_mcb_dlci_port (p_mcb, dlci)) == NULL) + return; + + p_port->tx.peer_fc = !enable_data; + } + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + /* If DLCI is 0 event applies to all ports */ + if (dlci == 0) + { + p_port = &rfc_cb.port.port[i]; + if (!p_port->in_use + || (p_port->rfc.p_mcb != p_mcb) + || (p_port->rfc.state != RFC_STATE_OPENED)) + continue; + } + events = 0; + + /* Check if flow of data is still enabled */ + events |= port_flow_control_user (p_port); + + /* Check if data can be sent and send it */ + events |= port_rfc_send_tx_data (p_port); + + /* Mask out all events that are not of interest to user */ + events &= p_port->ev_mask; + + /* Send event to the application */ + if (p_port->p_callback && events) + (p_port->p_callback)(events, p_port->inx); + + /* If DLCI is not 0 event applies to one port only */ + if (dlci != 0) + break; + } +} + + +/******************************************************************************* +** +** Function port_rfc_send_tx_data +** +** Description This function is when forward data can be sent to the peer +** +*******************************************************************************/ +UINT32 port_rfc_send_tx_data (tPORT *p_port) +{ + UINT32 events = 0; + BT_HDR *p_buf; + + /* if there is data to be sent */ + if (p_port->tx.queue_size > 0) + { + /* while the rfcomm peer is not flow controlling us, and peer is ready */ + while (!p_port->tx.peer_fc && p_port->rfc.p_mcb && p_port->rfc.p_mcb->peer_ready) + { + /* get data from tx queue and send it */ + PORT_SCHEDULE_LOCK; + + if ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + { + p_port->tx.queue_size -= p_buf->len; + + PORT_SCHEDULE_UNLOCK; + + RFCOMM_TRACE_DEBUG1 ("Sending RFCOMM_DataReq tx.queue_size=%d", p_port->tx.queue_size); + + RFCOMM_DataReq (p_port->rfc.p_mcb, p_port->dlci, p_buf); + + events |= PORT_EV_TXCHAR; + + if (p_port->tx.queue_size == 0) + { + events |= PORT_EV_TXEMPTY; + break; + } + } + /* queue is empty-- all data sent */ + else + { + PORT_SCHEDULE_UNLOCK; + + events |= PORT_EV_TXEMPTY; + break; + } + } + /* If we flow controlled user based on the queue size enable data again */ + events |= port_flow_control_user (p_port); + } + return (events & p_port->ev_mask); +} + + +/******************************************************************************* +** +** Function port_rfc_closed +** +** Description This function when RFCOMM side of port is closed +** +*******************************************************************************/ +void port_rfc_closed (tPORT *p_port, UINT8 res) +{ + UINT8 old_signals; + UINT32 events = 0; + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + if ((p_port->state == PORT_STATE_OPENING) && (p_port->is_server)) + { + /* The servr side has not been informed that connection is up, ignore */ + RFCOMM_TRACE_EVENT0 ("port_rfc_closed in OPENING state ignored"); + + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_CLOSED; + + if (p_mcb) + { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + p_port->rfc.p_mcb = NULL; + } + + /* Need to restore DLCI to listening state + * if the server was on the initiating RFC + */ + p_port->dlci &= 0xfe; + + return; + } + + if ((p_port->state != PORT_STATE_CLOSING) && (p_port->state != PORT_STATE_CLOSED)) + { + p_port->line_status |= LINE_STATUS_FAILED; + + old_signals = p_port->peer_ctrl.modem_signal; + + p_port->peer_ctrl.modem_signal &= ~(PORT_DTRDSR_ON | PORT_CTSRTS_ON | PORT_DCD_ON); + + events |= port_get_signal_changes (p_port, old_signals, p_port->peer_ctrl.modem_signal); + + if(p_port->ev_mask & PORT_EV_CONNECT_ERR) + events |= PORT_EV_CONNECT_ERR; + } + RFCOMM_TRACE_EVENT2 ("port_rfc_closed state:%d sending events:%x", p_port->state, events); + + if ((p_port->p_callback != NULL) && events) + p_port->p_callback (events, p_port->inx); + + if (p_port->p_mgmt_callback) + p_port->p_mgmt_callback (res, p_port->inx); + + p_port->rfc.state = RFC_STATE_CLOSED; + + port_release_port (p_port); +} + + +/******************************************************************************* +** +** Function port_get_credits +** +** Description Set initial values for credits. +** Adjust max number of rx credits based on negotiated MTU. +** Check max allowed num of bytes, max allowed num buffers, +** should be less then 255 +** +*******************************************************************************/ +void port_get_credits (tPORT *p_port, UINT8 k) +{ + p_port->credit_tx = k; + if (p_port->credit_tx == 0) + p_port->tx.peer_fc = TRUE; +} + + + diff --git a/stack/rfcomm/port_utils.c b/stack/rfcomm/port_utils.c new file mode 100644 index 0000000..ae41682 --- /dev/null +++ b/stack/rfcomm/port_utils.c @@ -0,0 +1,582 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * Port Emulation entity utilities + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "rfc_int.h" +#include "l2cdefs.h" +#include "btm_int.h" +#include "btu.h" + +#include +#define info(fmt, ...) ALOGI ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define debug(fmt, ...) ALOGD ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define error(fmt, ...) ALOGE ("## ERROR : %s: " fmt "##",__FUNCTION__, ## __VA_ARGS__) +#define asrt(s) if(!(s)) ALOGE ("## %s assert %s failed at line:%d ##",__FUNCTION__, #s, __LINE__) + + +static const tPORT_STATE default_port_pars = +{ + PORT_BAUD_RATE_9600, + PORT_8_BITS, + PORT_ONESTOPBIT, + PORT_PARITY_NO, + PORT_ODD_PARITY, + PORT_FC_OFF, + 0, /* No rx_char */ + PORT_XON_DC1, + PORT_XOFF_DC3, +}; + + + +/******************************************************************************* +** +** Function port_allocate_port +** +** Description Look through the Port Control Blocks for a free one. Note +** that one server can open several ports with the same SCN +** if it can support simulteneous requests from different +** clients. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr) +{ + tPORT *p_port = &rfc_cb.port.port[0]; + UINT8 xx, yy; + + for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) + { + if (yy >= MAX_RFC_PORTS) + yy = 0; + + p_port = &rfc_cb.port.port[yy]; + if (!p_port->in_use) + { + memset (p_port, 0, sizeof (tPORT)); + + p_port->in_use = TRUE; + p_port->inx = yy + 1; + + p_port->dlci = dlci; + memcpy (p_port->bd_addr, bd_addr, BD_ADDR_LEN); + + /* During the open set default state for the port connection */ + port_set_defaults (p_port); + + rfc_cb.rfc.last_port = yy; + debug("rfc_cb.port.port[%d] allocated, last_port:%d", yy, rfc_cb.rfc.last_port); + return (p_port); + } + } + + /* If here, no free PORT found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function port_set_defaults +** +** Description Set defualt port parameters +** +** +*******************************************************************************/ +void port_set_defaults (tPORT *p_port) +{ + p_port->ev_mask = 0; + p_port->p_callback = NULL; + p_port->port_ctrl = 0; + p_port->error = 0; + p_port->line_status = 0; + p_port->rx_flag_ev_pending = FALSE; + p_port->peer_mtu = RFCOMM_DEFAULT_MTU; + + p_port->user_port_pars = default_port_pars; + p_port->peer_port_pars = default_port_pars; + + p_port->credit_tx = 0; + p_port->credit_rx = 0; +/* p_port->credit_rx_max = PORT_CREDIT_RX_MAX; Determined later */ +/* p_port->credit_rx_low = PORT_CREDIT_RX_LOW; Determined later */ + + memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl)); + memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl)); + memset (&p_port->rx, 0, sizeof (p_port->rx)); + memset (&p_port->tx, 0, sizeof (p_port->tx)); +} + +/******************************************************************************* +** +** Function port_select_mtu +** +** Description Select MTU which will best serve connection from our +** point of view. +** If our device is 1.2 or lower we calculate how many DH5s +** fit into 1 RFCOMM buffer. +** +** +*******************************************************************************/ +void port_select_mtu (tPORT *p_port) +{ + UINT16 packet_size; + + /* Will select MTU only if application did not setup something */ + if (p_port->mtu == 0) + { + /* find packet size which connection supports */ + packet_size = btm_get_max_packet_size (p_port->bd_addr); + if (packet_size == 0) + { + /* something is very wrong */ + RFCOMM_TRACE_WARNING0 ("port_select_mtu bad packet size"); + p_port->mtu = RFCOMM_DEFAULT_MTU; + } + else + { + /* We try to negotiate MTU that each packet can be split into whole + number of max packets. For example if link is 1.2 max packet size is 339 bytes. + At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead. + 1695, that will be 5 Dh5 packets. Now maximum RFCOMM packet is + 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. Minus RFCOMM 6 bytes header overhead 1685 + + For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet + 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. Minus RFCOMM 6 bytes header overhead 1017 */ + if ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) >= packet_size) + { + p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size * packet_size) - RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD; + RFCOMM_TRACE_DEBUG1 ("port_select_mtu selected %d based on connection speed", p_port->mtu); + } + else + { + p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD; + RFCOMM_TRACE_DEBUG1 ("port_select_mtu selected %d based on l2cap PDU size", p_port->mtu); + } + } + } + else + { + RFCOMM_TRACE_DEBUG1 ("port_select_mtu application selected %d", p_port->mtu); + } + p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu); + if( p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM ) + p_port->credit_rx_max = PORT_RX_BUF_HIGH_WM; + p_port->credit_rx_low = (PORT_RX_LOW_WM / p_port->mtu); + if( p_port->credit_rx_low > PORT_RX_BUF_LOW_WM ) + p_port->credit_rx_low = PORT_RX_BUF_LOW_WM; + p_port->rx_buf_critical = (PORT_RX_CRITICAL_WM / p_port->mtu); + if( p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM ) + p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM; + RFCOMM_TRACE_DEBUG3 ("port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d", + p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical); +} + + +/******************************************************************************* +** +** Function port_release_port +** +** Description Release port infor control block. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +void port_release_port (tPORT *p_port) +{ + BT_HDR *p_buf; + UINT32 mask; + tPORT_CALLBACK *p_port_cb; + tPORT_STATE user_port_pars; + + PORT_SCHEDULE_LOCK; + debug("port_release_port, p_port:%p", p_port); + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->rx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->rx.queue_size = 0; + + while ((p_buf = (BT_HDR *)GKI_dequeue (&p_port->tx.queue)) != NULL) + GKI_freebuf (p_buf); + + p_port->tx.queue_size = 0; + + PORT_SCHEDULE_UNLOCK; + + p_port->state = PORT_STATE_CLOSED; + + if (p_port->rfc.state == RFC_STATE_CLOSED) + { + RFCOMM_TRACE_DEBUG0 ("rfc_port_closed DONE"); + if (p_port->rfc.p_mcb) + { + p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_port->rfc.p_mcb); + } + rfc_port_timer_stop (p_port); + + if( p_port->keep_port_handle ) + { + RFCOMM_TRACE_DEBUG1 ("port_release_port:Initialize handle:%d", p_port->inx); + /* save event mask and callback */ + mask = p_port->ev_mask; + p_port_cb = p_port->p_callback; + user_port_pars = p_port->user_port_pars; + + port_set_defaults(p_port); + /* restore */ + p_port->ev_mask = mask; + p_port->p_callback = p_port_cb; + p_port->user_port_pars = user_port_pars; + p_port->mtu = p_port->keep_mtu; + + p_port->state = PORT_STATE_OPENING; + p_port->rfc.p_mcb = NULL; + if(p_port->is_server) + p_port->dlci &= 0xfe; + + p_port->local_ctrl.modem_signal = p_port->default_signal_state; + memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN); + } + else + { + RFCOMM_TRACE_DEBUG1 ("port_release_port:Clean-up handle:%d", p_port->inx); + memset (p_port, 0, sizeof (tPORT)); + } + } +} + + +/******************************************************************************* +** +** Function port_find_mcb +** +** Description This function checks if connection exists to device with +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *port_find_mcb (BD_ADDR bd_addr) +{ + int i; + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) + { + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && !memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN)) + { + /* Multiplexer channel found do not change anything */ + return (&rfc_cb.port.rfc_mcb[i]); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_mcb_dlci_port +** +** Description Find port on the multiplexer channel based on DLCI. If +** this port with DLCI not found try to use even DLCI. This +** is for the case when client is establishing connection on +** none-initiator MCB. +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci) +{ + UINT8 inx; + + if (!p_mcb) + return (NULL); + + if (dlci > RFCOMM_MAX_DLCI) + return (NULL); + + inx = p_mcb->port_inx[dlci]; + if (inx == 0) + return (NULL); + else + return (&rfc_cb.port.port[inx - 1]); +} + + +/******************************************************************************* +** +** Function port_find_dlci_port +** +** Description Find port with DLCI not assigned to multiplexer channel +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_dlci_port (UINT8 dlci) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use && (p_port->rfc.p_mcb == NULL)) + { + if (p_port->dlci == dlci) + { + return (p_port); + } + else if ((dlci & 0x01) && (p_port->dlci == (dlci - 1))) + { + p_port->dlci++; + return (p_port); + } + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_find_port +** +** Description Find port with DLCI, BD_ADDR +** +** Returns Pointer to the PORT or NULL if not found +** +*******************************************************************************/ +tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr) +{ + UINT16 i; + tPORT *p_port; + + for (i = 0; i < MAX_RFC_PORTS; i++) + { + p_port = &rfc_cb.port.port[i]; + if (p_port->in_use + && (p_port->dlci == dlci) + && !memcmp (p_port->bd_addr, bd_addr, BD_ADDR_LEN)) + { + return (p_port); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function port_flow_control_user +** +** Description Check the current user flow control and if necessary return +** events to be send to the user based on the user's specified +** flow control type. +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_flow_control_user (tPORT *p_port) +{ + UINT32 event = 0; + + /* Flow control to the user can be caused by flow controlling by the peer */ + /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */ + /* tx_queue is full */ + BOOLEAN fc = p_port->tx.peer_fc + || !p_port->rfc.p_mcb + || !p_port->rfc.p_mcb->peer_ready + || (p_port->tx.queue_size > PORT_TX_HIGH_WM) + || (p_port->tx.queue.count > PORT_TX_BUF_HIGH_WM); + + if (p_port->tx.user_fc == fc) + return (0); + + p_port->tx.user_fc = fc; + + if (fc) + event = PORT_EV_FC; + else + event = PORT_EV_FC | PORT_EV_FCS; + + return (event); +} + + +/******************************************************************************* +** +** Function port_get_signal_changes +** +** Description Check modem signals that has been changed +** +** Returns event mask to be returned to the application +** +*******************************************************************************/ +UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal) +{ + UINT8 changed_signals = (signal ^ old_signals); + UINT32 events = 0; + + if (changed_signals & PORT_DTRDSR_ON) + { + events |= PORT_EV_DSR; + + if (signal & PORT_DTRDSR_ON) + events |= PORT_EV_DSRS; + } + + if (changed_signals & PORT_CTSRTS_ON) + { + events |= PORT_EV_CTS; + + if (signal & PORT_CTSRTS_ON) + events |= PORT_EV_CTSS; + } + + if (changed_signals & PORT_RING_ON) + events |= PORT_EV_RING; + + if (changed_signals & PORT_DCD_ON) + { + events |= PORT_EV_RLSD; + + if (signal & PORT_DCD_ON) + events |= PORT_EV_RLSDS; + } + + return (p_port->ev_mask & events); +} + +/******************************************************************************* +** +** Function port_flow_control_peer +** +** Description Send flow control messages to the peer for both enabling +** and disabling flow control, for both credit-based and +** TS 07.10 flow control mechanisms. +** +** Returns nothing +** +*******************************************************************************/ +void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count) +{ + if (!p_port->rfc.p_mcb) + return; + + /* If using credit based flow control */ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + /* if want to enable flow from peer */ + if (enable) + { + /* update rx credits */ + if (count > p_port->credit_rx) + { + p_port->credit_rx = 0; + } + else + { + p_port->credit_rx -= count; + } + + /* If credit count is less than low credit watermark, and user */ + /* did not force flow control, send a credit update */ + /* There might be a special case when we just adjusted rx_max */ + if ((p_port->credit_rx <= p_port->credit_rx_low) + && !p_port->rx.user_fc + && (p_port->credit_rx_max > p_port->credit_rx)) + { + rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci, + (UINT8) (p_port->credit_rx_max - p_port->credit_rx)); + + p_port->credit_rx = p_port->credit_rx_max; + + p_port->rx.peer_fc = FALSE; + } + } + /* else want to disable flow from peer */ + else + { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback || p_port->p_data_co_callback) + { + p_port->rx.peer_fc = TRUE; + } + /* if queue count reached credit rx max, set peer fc */ + else if (p_port->rx.queue.count >= p_port->credit_rx_max) + { + p_port->rx.peer_fc = TRUE; + } + } + } + /* else using TS 07.10 flow control */ + else + { + /* if want to enable flow from peer */ + if (enable) + { + /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ + /* check if it can be resumed now */ + if (p_port->rx.peer_fc + && (p_port->rx.queue_size < PORT_RX_LOW_WM) + && (p_port->rx.queue.count < PORT_RX_BUF_LOW_WM)) + { + p_port->rx.peer_fc = FALSE; + + /* If user did not force flow control allow traffic now */ + if (!p_port->rx.user_fc) + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + } + /* else want to disable flow from peer */ + else + { + /* if client registered data callback, just do what they want */ + if (p_port->p_data_callback || p_port->p_data_co_callback) + { + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + /* Check the size of the rx queue. If it exceeds certain */ + /* level and flow control has not been sent to the peer do it now */ + else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM) + || (p_port->rx.queue.count > PORT_RX_BUF_HIGH_WM)) + && !p_port->rx.peer_fc) + { + RFCOMM_TRACE_EVENT0 ("PORT_DataInd Data reached HW. Sending FC set."); + + p_port->rx.peer_fc = TRUE; + RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); + } + } + } +} + diff --git a/stack/rfcomm/rfc_int.h b/stack/rfcomm/rfc_int.h new file mode 100644 index 0000000..83a63e3 --- /dev/null +++ b/stack/rfcomm/rfc_int.h @@ -0,0 +1,387 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains definitions internal to the RFC unit + * + *****************************************************************************/ + +#ifndef RFC_INT_H +#define RFC_INT_H + +#include "l2c_api.h" +#include "port_int.h" + +/* +** Define RFCOMM result codes +*/ +#define RFCOMM_SUCCESS 0 +#define RFCOMM_ERROR 1 +#define RFCOMM_LOW_RESOURCES 2 +#define RFCOMM_TRY_LATER 3 + +#define RFCOMM_USER_ERR 111 +#define RFCOMM_SECURITY_ERR 112 + +/* +** Define max and min RFCOMM MTU (N1) +*/ +#define RFCOMM_MIN_MTU 23 +#define RFCOMM_MAX_MTU 32767 + +extern void RFCOMM_StartReq (tRFC_MCB *p_mcb); +extern void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result); + +extern void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void RFCOMM_TestReq (UINT8 *p_data, UINT16 len); + +#define RFCOMM_FLOW_STATE_DISABLE 0 +#define RFCOMM_FLOW_STATE_ENABLE 1 + +extern void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 state); + +extern void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars); +extern void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); + +extern void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void RFCOMM_ControlRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); +/* +** Define logical struct used for sending and decoding MX frames +*/ +typedef struct +{ + UINT8 dlci; + UINT8 type; + UINT8 cr; + UINT8 ea; + UINT8 pf; + UINT8 credit; + + union + { + struct + { + UINT8 dlci; + UINT8 frame_type; + UINT8 conv_layer; + UINT8 priority; + UINT8 t1; + UINT16 mtu; + UINT8 n2; + UINT8 k; + } pn; + struct + { + UINT8 *p_data; + UINT16 data_len; + } test; + struct + { + UINT8 dlci; + UINT8 signals; + UINT8 break_present; + UINT8 break_duration; + } msc; + struct + { + UINT8 ea; + UINT8 cr; + UINT8 type; + } nsc; + struct + { + UINT8 dlci; + UINT8 is_request; + UINT8 baud_rate; + UINT8 byte_size; + UINT8 stop_bits; + UINT8 parity; + UINT8 parity_type; + UINT8 fc_type; + UINT8 xon_char; + UINT8 xoff_char; + UINT16 param_mask; + } rpn; + struct + { + UINT8 dlci; + UINT8 line_status; + } rls; + } u; +} MX_FRAME; + +#define LINE_STATUS_NO_ERROR 0x00 +#define LINE_STATUS_OVERRUN 0x02 /* Receive Overrun Error */ +#define LINE_STATUS_RXPARITY 0x04 /* Receive Parity Error */ +#define LINE_STATUS_FRAME 0x08 /* Receive Framing error */ +#define LINE_STATUS_FAILED 0x10 /* Connection Failed */ + +/* +** Define states and events for the RFC multiplexer state machine +*/ +#define RFC_MX_STATE_IDLE 0 +#define RFC_MX_STATE_WAIT_CONN_CNF 1 +#define RFC_MX_STATE_CONFIGURE 2 +#define RFC_MX_STATE_SABME_WAIT_UA 3 +#define RFC_MX_STATE_WAIT_SABME 4 +#define RFC_MX_STATE_CONNECTED 5 +#define RFC_MX_STATE_DISC_WAIT_UA 6 + +/* +** Define port states +*/ +#define RFC_STATE_CLOSED 0 +#define RFC_STATE_SABME_WAIT_UA 1 +#define RFC_STATE_ORIG_WAIT_SEC_CHECK 2 +#define RFC_STATE_TERM_WAIT_SEC_CHECK 3 +#define RFC_STATE_OPENED 4 +#define RFC_STATE_DISC_WAIT_UA 5 + +/* +** Events that can be received by multiplexer as well as port state machines +*/ +#define RFC_EVENT_SABME 0 +#define RFC_EVENT_UA 1 +#define RFC_EVENT_DM 2 +#define RFC_EVENT_DISC 3 +#define RFC_EVENT_UIH 4 +#define RFC_EVENT_TIMEOUT 5 +#define RFC_EVENT_BAD_FRAME 50 +/* +** Multiplexer events +*/ +#define RFC_MX_EVENT_START_REQ 6 +#define RFC_MX_EVENT_START_RSP 7 +#define RFC_MX_EVENT_CLOSE_REQ 8 +#define RFC_MX_EVENT_CONN_CNF 9 +#define RFC_MX_EVENT_CONN_IND 10 +#define RFC_MX_EVENT_CONF_CNF 11 +#define RFC_MX_EVENT_CONF_IND 12 +#define RFC_MX_EVENT_QOS_VIOLATION_IND 13 +#define RFC_MX_EVENT_DISC_IND 14 +#define RFC_MX_EVENT_TEST_CMD 15 +#define RFC_MX_EVENT_TEST_RSP 16 +#define RFC_MX_EVENT_FCON_CMD 17 +#define RFC_MX_EVENT_FCOFF_CMD 18 +#define RFC_MX_EVENT_NSC 19 +#define RFC_MX_EVENT_NSC_RSP 20 + +/* +** Port events +*/ +#define RFC_EVENT_OPEN 9 +#define RFC_EVENT_ESTABLISH_RSP 11 +#define RFC_EVENT_CLOSE 12 +#define RFC_EVENT_CLEAR 13 +#define RFC_EVENT_DATA 14 +#define RFC_EVENT_SEC_COMPLETE 15 + +// btla-specific ++ +#define RFC_T1_TIMEOUT 20 /* seconds to wait for reply with Poll bit */ +#define RFC_PORT_T1_TIMEOUT 60 /* seconds to wait for reply with Poll bit other than MX */ +#define RFC_T2_TIMEOUT 20 /* timeout to wait for Mx UIH */ +// btla-specific -- +#define RFC_DISC_TIMEOUT 3 /* If something goes wrong and we send DISC we should not wait for min */ +#define RFC_CLOSE_TIMEOUT 10 +#define RFCOMM_CONN_TIMEOUT 120 /* first connection to be established on Mx */ + + +/* Define RFComm control block +*/ +typedef struct +{ + MX_FRAME rx_frame; + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ + tRFC_MCB *p_rfc_lcid_mcb[MAX_L2CAP_CHANNELS]; /* MCB based on the L2CAP's lcid */ + BOOLEAN peer_rx_disabled; /* If TRUE peer sent FCOFF */ + UINT8 last_mux; /* Last mux allocated */ + UINT8 last_port; /* Last port allocated */ +} tRFCOMM_CB; + +/* Main Control Block for the RFCOMM Layer (PORT and RFC) */ +typedef struct +{ + tRFCOMM_CB rfc; + tPORT_CB port; + UINT8 trace_level; +} tRFC_CB; + + +#if RFC_DYNAMIC_MEMORY == FALSE +RFC_API extern tRFC_CB rfc_cb; +#else +RFC_API extern tRFC_CB *rfc_cb_ptr; +#define rfc_cb (*rfc_cb_ptr) +#endif + +/* Timer running on the multiplexor channel while no DLCI connection is opened */ +#define RFC_MCB_INIT_INACT_TIMER 60 /* in seconds */ + +/* Timer running on the multiplexor channel after last DLCI is released */ +#define RFC_MCB_RELEASE_INACT_TIMER 2 /* in seconds */ + +/* +** Define RFCOMM frame processing errors +*/ +#define RFCOMM_ERR_BAD_SABME 1 +#define RFCOMM_ERR_BAD_UA 2 +#define RFCOMM_ERR_BAD_DM 3 +#define RFCOMM_ERR_BAD_DISC 4 +#define RFCOMM_ERR_BAD_UIH 5 + +#ifdef RFCOMM_PRECALC_FCS + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_sabme_fcs[cr][dlci] +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_ua_fcs[cr][dlci] +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_dm_fcs[cr][dlci] +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_disc_fcs[cr][dlci] +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_uih_fcs[dlci] + +#else + +extern UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p); + +#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data) +#define RFCOMM_UIH_FCS(p_data, dlci) rfc_calc_fcs(2, p_data) + +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +/* +** Functions provided by the rfc_port_fsm.c +*/ +extern void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data); + + +extern void rfc_process_pn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_msc (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_rpn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BOOLEAN is_request, MX_FRAME *p_frame); +extern void rfc_process_rls (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame); +extern void rfc_process_nsc (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame); +extern void rfc_process_test_rsp (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern void rfc_process_fcon (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_fcoff (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command); +extern void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested); + +/* +** Functions provided by the rfc_utils.c +*/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator); +extern void rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb); +extern void rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout); +extern void rfc_timer_stop (tRFC_MCB *p_rfc_mcb); +extern void rfc_port_timer_start (tPORT *p_port, UINT16 tout); +extern void rfc_port_timer_stop (tPORT *p_port); + +BOOLEAN rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs); +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs); +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid); +extern void rfc_save_lcid_mcb (tRFC_MCB *p_rfc_mcb, UINT16 lcid); +extern void rfc_check_mcb_active (tRFC_MCB *p_mcb); +extern void rfc_port_closed (tPORT *p_port); +extern void rfc_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res); +extern void rfc_inc_credit (tPORT *p_port, UINT8 credit); +extern void rfc_dec_credit (tPORT *p_port); +extern void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_ts_frames.c +*/ +extern void rfc_send_sabme (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_ua (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_dm (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BOOLEAN pf); +extern void rfc_send_disc (tRFC_MCB *p_rfc_mcb, UINT8 dlci); +extern void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, + UINT8 cl, UINT8 k); +extern void rfc_send_test (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BT_HDR *p_buf); +extern void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars); +extern void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status); +extern void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask); +extern void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command); +extern void rfc_send_buf_uih (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BT_HDR *p_buf); +extern void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit); +extern void rfc_process_mx_message (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf); +extern UINT8 rfc_parse_data (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame, BT_HDR *p_buf); + +/* +** Functions provided by the rfc_disp.c +*/ + + + +/* Call back functions from RFCOMM */ +extern void rfcomm_l2cap_if_init (void); + +extern void PORT_StartInd (tRFC_MCB *p_mcb); +extern void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result); + +extern void PORT_CloseInd (tRFC_MCB *p_mcb); +extern void Port_TimeOutCloseMux (tRFC_MCB *p_mcb); + +extern void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu); +extern void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result); + +extern void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf); + +extern void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci); + +extern void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); +extern void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k); + +extern void PORT_TestCnf (tRFC_MCB *p_mcb, UINT8 *p_data, UINT16 len); + +extern void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN fc); + +extern void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask); +extern void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result); + +extern void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); +extern void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars); + +extern void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/rfcomm/rfc_l2cap_if.c b/stack/rfcomm/rfc_l2cap_if.c new file mode 100644 index 0000000..cb50612 --- /dev/null +++ b/stack/rfcomm/rfc_l2cap_if.c @@ -0,0 +1,443 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains L2CAP interface functions + * + ******************************************************************************/ + +#include "bt_target.h" +#include "gki.h" + +#include "rfcdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "rfc_int.h" + + +/* +** Define Callback functions to be called by L2CAP +*/ +static void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +static void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 err); +static void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +static void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_clear); +static void RFCOMM_QoSViolationInd (BD_ADDR bd_addr); +static void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf); +static void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested); + + +/******************************************************************************* +** +** Function rfcomm_l2cap_if_init +** +** Description This function is called during the RFCOMM task startup +** to register interface functions with L2CAP. +** +*******************************************************************************/ +void rfcomm_l2cap_if_init (void) +{ + tL2CAP_APPL_INFO *p_l2c = &rfc_cb.rfc.reg_info; + + p_l2c->pL2CA_ConnectInd_Cb = RFCOMM_ConnectInd; + p_l2c->pL2CA_ConnectCfm_Cb = RFCOMM_ConnectCnf; + p_l2c->pL2CA_ConnectPnd_Cb = NULL; + p_l2c->pL2CA_ConfigInd_Cb = RFCOMM_ConfigInd; + p_l2c->pL2CA_ConfigCfm_Cb = RFCOMM_ConfigCnf; + p_l2c->pL2CA_DisconnectInd_Cb = RFCOMM_DisconnectInd; + p_l2c->pL2CA_DisconnectCfm_Cb = NULL; + p_l2c->pL2CA_QoSViolationInd_Cb = RFCOMM_QoSViolationInd; + p_l2c->pL2CA_DataInd_Cb = RFCOMM_BufDataInd; + p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd; + p_l2c->pL2CA_TxComplete_Cb = NULL; + + + L2CA_Register (BT_PSM_RFCOMM, p_l2c); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectInd received. Allocate multiplexer control block +** and dispatch the event to it. +** +*******************************************************************************/ +void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tRFC_MCB *p_mcb = rfc_alloc_multiplexer_channel(bd_addr, FALSE); + + if ((p_mcb)&&(p_mcb->state != RFC_MX_STATE_IDLE)) + { + /* if this is collision case */ + if ((p_mcb->is_initiator)&&(p_mcb->state == RFC_MX_STATE_WAIT_CONN_CNF)) + { + p_mcb->pending_lcid = lcid; + p_mcb->pending_id = id; + + /* wait random timeout (2 - 12) to resolve collision */ + /* if peer gives up then local device rejects incoming connection and continues as initiator */ + /* if timeout, local device disconnects outgoing connection and continues as acceptor */ + RFCOMM_TRACE_DEBUG2 ("RFCOMM_ConnectInd start timer for collision, initiator's LCID(0x%x), acceptor's LCID(0x%x)", + p_mcb->lcid, p_mcb->pending_lcid); + + rfc_timer_start(p_mcb, (UINT16)(GKI_get_tick_count()%10 + 2)); + return; + } + else + { + /* we cannot accept connection request from peer at this state */ + /* don't update lcid */ + p_mcb = NULL; + } + } + else + { + /* store mcb even if null */ + rfc_save_lcid_mcb (p_mcb, lcid); + } + + if (p_mcb == NULL) + { + L2CA_ConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &id); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConnectCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConnectCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConnectCnf LCID:0x%x", lcid); + return; + } + + if (p_mcb->pending_lcid) + { + /* if peer rejects our connect request but peer's connect request is pending */ + if (result != L2CAP_CONN_OK ) + { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG1 ("RFCOMM_ConnectCnf retry as acceptor on pending LCID(0x%x)", p_mcb->pending_lcid); + + /* remove mcb from mapping table */ + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + + p_mcb->lcid = p_mcb->pending_lcid; + p_mcb->is_initiator = FALSE; + p_mcb->state = RFC_MX_STATE_IDLE; + + /* store mcb into mapping table */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) + { + if ((idx = p_mcb->port_inx[i]) != 0) + { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i+1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + return; + } + else + { + RFCOMM_TRACE_DEBUG1 ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid); + + /* Peer gave up his connection request, make sure cleaning up L2CAP channel */ + L2CA_ConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0); + + p_mcb->pending_lcid = 0; + } + } + + /* Save LCID to be used in all consecutive calls to L2CAP */ + p_mcb->lcid = lcid; + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_CNF, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigInd +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigInd received. Save parameters in the control +** block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConfigInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_IND, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_ConfigCnf +** +** Description This is a callback function called by L2CAP when +** L2CA_ConfigCnf received. Save L2CAP handle and dispatch +** event to the FSM. +** +*******************************************************************************/ +void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_ConfigCnf no MCB LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_CNF, (void *)p_cfg); +} + + +/******************************************************************************* +** +** Function RFCOMM_QoSViolationInd +** +** Description This is a callback function called by L2CAP when +** L2CA_QoSViolationIndInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_QoSViolationInd (BD_ADDR bd_addr) +{ +} + + +/******************************************************************************* +** +** Function RFCOMM_DisconnectInd +** +** Description This is a callback function called by L2CAP when +** L2CA_DisconnectInd received. Dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_conf_needed) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (is_conf_needed) + { + L2CA_DisconnectRsp (lcid); + } + + if (!p_mcb) + { + RFCOMM_TRACE_WARNING1 ("RFCOMM_DisconnectInd LCID:0x%x", lcid); + return; + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_DISC_IND, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_BufDataInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM frame is received. Parse the frames, check +** the checksum and dispatch event to multiplexer or port +** state machine depending on the frame destination. +** +*******************************************************************************/ +void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + tPORT *p_port; + UINT8 event; + + + if (!p_mcb) + { + RFCOMM_TRACE_WARNING1 ("RFCOMM_BufDataInd LCID:0x%x", lcid); + GKI_freebuf (p_buf); + return; + } + + event = rfc_parse_data (p_mcb, &rfc_cb.rfc.rx_frame, p_buf); + + /* If the frame did not pass validation just ignore it */ + if (event == RFC_EVENT_BAD_FRAME) + { + GKI_freebuf (p_buf); + return; + } + + if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) + { + /* Take special care of the Multiplexer Control Messages */ + if (event == RFC_EVENT_UIH) + { + rfc_process_mx_message (p_mcb, p_buf); + return; + } + + /* Other multiplexer events go to state machine */ + rfc_mx_sm_execute (p_mcb, event, NULL); + GKI_freebuf (p_buf); + return; + } + + /* The frame was received on the data channel DLCI, verify that DLC exists */ + if (((p_port = port_find_mcb_dlci_port (p_mcb, rfc_cb.rfc.rx_frame.dlci)) == NULL) + || (!p_port->rfc.p_mcb)) + { + /* If this is a SABME on the new port, check if any appl is waiting for it */ + if (event != RFC_EVENT_SABME) + { + if (( p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr) + || (!p_mcb->is_initiator && rfc_cb.rfc.rx_frame.cr)) + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf); + GKI_freebuf (p_buf); + return; + } + + if ((p_port = port_find_dlci_port (rfc_cb.rfc.rx_frame.dlci)) == NULL) + { + rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, TRUE); + GKI_freebuf (p_buf); + return; + } + p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx; + p_port->rfc.p_mcb = p_mcb; + } + + if (event == RFC_EVENT_UIH) + { + if (p_buf->len > 0) + rfc_port_sm_execute (p_port, event, p_buf); + else + GKI_freebuf (p_buf); + + if (rfc_cb.rfc.rx_frame.credit != 0) + rfc_inc_credit (p_port, rfc_cb.rfc.rx_frame.credit); + + return; + } + rfc_port_sm_execute (p_port, event, NULL); + GKI_freebuf (p_buf); +} + +/******************************************************************************* +** +** Function RFCOMM_CongestionStatusInd +** +** Description This is a callback function called by L2CAP when +** data RFCOMM L2CAP congestion status changes +** +*******************************************************************************/ +void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested) +{ + tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); + + if (!p_mcb) + { + RFCOMM_TRACE_ERROR1 ("RFCOMM_CongestionStatusInd dropped LCID:0x%x", lcid); + return; + } + else + { + RFCOMM_TRACE_EVENT1 ("RFCOMM_CongestionStatusInd LCID:0x%x", lcid); + } + rfc_process_l2cap_congestion (p_mcb, is_congested); +} + +/******************************************************************************* +** +** Function rfc_find_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid) +{ + tRFC_MCB *p_mcb; + + if (lcid - L2CAP_BASE_APPL_CID >= MAX_L2CAP_CHANNELS) + { + RFCOMM_TRACE_ERROR1 ("rfc_find_lcid_mcb LCID:0x%x", lcid); + return (NULL); + } + else + { + if ((p_mcb = rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID]) != NULL) + { + if (p_mcb->lcid != lcid) + { + RFCOMM_TRACE_WARNING2 ("rfc_find_lcid_mcb LCID reused LCID:0x%x current:0x%x", lcid, p_mcb->lcid); + return (NULL); + } + } + } + return (p_mcb); +} + + +/******************************************************************************* +** +** Function rfc_save_lcid_mcb +** +** Description This function returns MCB block supporting local cid +** +*******************************************************************************/ +void rfc_save_lcid_mcb (tRFC_MCB *p_mcb, UINT16 lcid) +{ + rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb; +} diff --git a/stack/rfcomm/rfc_mx_fsm.c b/stack/rfcomm/rfc_mx_fsm.c new file mode 100644 index 0000000..842f8ee --- /dev/null +++ b/stack/rfcomm/rfc_mx_fsm.c @@ -0,0 +1,663 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains state machine and action routines for multiplexer + * channel of the RFCOMM unit + * + ******************************************************************************/ +#include +#include "gki.h" +#include "bt_types.h" +#include "rfcdefs.h" +#include "l2cdefs.h" +#include "port_api.h" +#include "port_int.h" +#include "l2c_api.h" +#include "rfc_int.h" + +#define L2CAP_SUCCESS 0 +#define L2CAP_ERROR 1 + + +/********************************************************************************/ +/* 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 rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data); +static void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data); + +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb); +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg); + + + +/******************************************************************************* +** +** Function rfc_mx_sm_execute +** +** Description This function sends multiplexor events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + switch (p_mcb->state) + { + case RFC_MX_STATE_IDLE: + rfc_mx_sm_state_idle (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_CONN_CNF: + rfc_mx_sm_state_wait_conn_cnf (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONFIGURE: + rfc_mx_sm_state_configure (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_SABME_WAIT_UA: + rfc_mx_sm_sabme_wait_ua (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_WAIT_SABME: + rfc_mx_sm_state_wait_sabme (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_CONNECTED: + rfc_mx_sm_state_connected (p_mcb, event, p_data); + break; + + case RFC_MX_STATE_DISC_WAIT_UA: + rfc_mx_sm_state_disc_wait_ua (p_mcb, event, p_data); + break; + + } +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_idle +** +** Description This function handles events when the multiplexer is in +** IDLE state. This state exists when connection is being +** initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_idle - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + + /* Initialize L2CAP MTU */ + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + + if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) + { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + + case RFC_MX_EVENT_START_RSP: + case RFC_MX_EVENT_CONN_CNF: + case RFC_MX_EVENT_CONF_IND: + case RFC_MX_EVENT_CONF_CNF: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONN_IND: + + rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); + L2CA_ConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0); + + rfc_mx_send_config_req (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONFIGURE; + return; + + case RFC_EVENT_SABME: + break; + + case RFC_EVENT_UA: + case RFC_EVENT_DM: + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + return; + + case RFC_EVENT_UIH: + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_conn_cnf +** +** Description This function handles events when the multiplexer is +** waiting for Connection Confirm from L2CAP. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_wait_conn_cnf - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* There is some new timing so that Config Ind comes before security is completed + so we are still waiting fo the confirmation. */ + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONN_CNF: + if (*((UINT16 *)p_data) != L2CAP_SUCCESS) + { + p_mcb->state = RFC_MX_STATE_IDLE; + + PORT_StartCnf (p_mcb, *((UINT16 *)p_data)); + return; + } + p_mcb->state = RFC_MX_STATE_CONFIGURE; + rfc_mx_send_config_req (p_mcb); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + /* we gave up outgoing connection request then try peer's request */ + if (p_mcb->pending_lcid) + { + UINT16 i; + UINT8 idx; + + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX retry as acceptor in collision case - evt:%d in state:%d", event, p_mcb->state); + + rfc_save_lcid_mcb (NULL, p_mcb->lcid); + p_mcb->lcid = p_mcb->pending_lcid; + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + p_mcb->is_initiator = FALSE; + + /* update direction bit */ + for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) + { + if ((idx = p_mcb->port_inx[i]) != 0) + { + p_mcb->port_inx[i] = 0; + p_mcb->port_inx[i+1] = idx; + rfc_cb.port.port[idx - 1].dlci += 1; + RFCOMM_TRACE_DEBUG2 ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci); + } + } + + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id)); + } + else + { + PORT_CloseInd (p_mcb); + } + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_configure +** +** Description This function handles events when the multiplexer in the +** configuration state. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_configure - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_sabme_wait_ua +** +** Description This function handles events when the multiplexer sent +** SABME and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_sabme_wait_ua - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_START_REQ: + case RFC_MX_EVENT_CONN_CNF: + RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); + return; + + /* workaround: we don't support reconfig */ + /* commented out until we support reconfig + case RFC_MX_EVENT_CONF_IND: + rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + + case RFC_MX_EVENT_CONF_CNF: + rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data); + return; + */ + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_UA: + rfc_timer_stop (p_mcb); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + rfc_timer_stop (p_mcb); + /* Case falls through */ + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + +/******************************************************************************* +** +** Function rfc_mx_sm_state_wait_sabme +** +** Description This function handles events when the multiplexer is +** waiting for SABME on the acceptor side after configuration +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_wait_sabme - evt:%d", event); + switch (event) + { + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_SABME: + /* if we gave up outgoing connection request */ + if (p_mcb->pending_lcid) + { + p_mcb->pending_lcid = 0; + + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + rfc_timer_stop (p_mcb); + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + + /* MX channel collision has been resolved, continue to open ports */ + PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); + } + else + { + rfc_timer_stop (p_mcb); + PORT_StartInd (p_mcb); + } + return; + + case RFC_MX_EVENT_START_RSP: + if (*((UINT16 *)p_data) != RFCOMM_SUCCESS) + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); + else + { + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + + p_mcb->state = RFC_MX_STATE_CONNECTED; + p_mcb->peer_ready = TRUE; + } + return; + + case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ + case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ + case RFC_EVENT_TIMEOUT: + p_mcb->state = RFC_MX_STATE_IDLE; + L2CA_DisconnectReq (p_mcb->lcid); + + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_connected +** +** Description This function handles events when the multiplexer is +** in the CONNECTED state +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_connected - evt:%d", event); + + switch (event) + { + case RFC_EVENT_TIMEOUT: + case RFC_MX_EVENT_CLOSE_REQ: + rfc_timer_start (p_mcb, RFC_DISC_TIMEOUT); + p_mcb->state = RFC_MX_STATE_DISC_WAIT_UA; + rfc_send_disc (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_EVENT_DISC: + /* Reply with UA. If initiator bring down L2CAP connection */ + /* If server wait for some time if client decide to reinitiate channel */ + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + if (p_mcb->is_initiator) + { + L2CA_DisconnectReq (p_mcb->lcid); + } + /* notify all ports that connection is gone */ + PORT_CloseInd (p_mcb); + return; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_sm_state_disc_wait_ua +** +** Description This function handles events when the multiplexer sent +** DISC and is waiting for UA reply. +** +** Returns void +** +*******************************************************************************/ +void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) +{ + BT_HDR *p_buf; + + RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_disc_wait_ua - evt:%d", event); + switch (event) + { + case RFC_EVENT_UA: + case RFC_EVENT_DM: + case RFC_EVENT_TIMEOUT: + L2CA_DisconnectReq (p_mcb->lcid); + + if (p_mcb->restart_required) + { + /* Start Request was received while disconnecting. Execute it again */ + if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) + { + PORT_StartCnf (p_mcb, RFCOMM_ERROR); + return; + } + /* Save entry for quicker access to mcb based on the LCID */ + rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); + + /* clean up before reuse it */ + while ((p_buf = (BT_HDR *)GKI_dequeue(&p_mcb->cmd_q)) != NULL) + GKI_freebuf(p_buf); + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + p_mcb->is_initiator = TRUE; + p_mcb->restart_required = FALSE; + p_mcb->local_cfg_sent = FALSE; + p_mcb->peer_cfg_rcvd = FALSE; + + p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; + return; + } + rfc_release_multiplexer_channel (p_mcb); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); + return; + + case RFC_MX_EVENT_START_REQ: + p_mcb->restart_required = TRUE; + return; + + case RFC_MX_EVENT_DISC_IND: + p_mcb->state = RFC_MX_STATE_IDLE; + PORT_CloseInd (p_mcb); + return; + + case RFC_MX_EVENT_CLOSE_REQ: + return; + + case RFC_MX_EVENT_QOS_VIOLATION_IND: + break; + } + RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); +} + + +/******************************************************************************* +** +** Function rfc_mx_send_config_req +** +** Description This function handles L2CA_ConnectInd message from the +** L2CAP. Accept connection. +** +*******************************************************************************/ +static void rfc_mx_send_config_req (tRFC_MCB *p_mcb) +{ + tL2CAP_CFG_INFO cfg; + + RFCOMM_TRACE_EVENT0 ("rfc_mx_send_config_req"); + + memset (&cfg, 0, sizeof (tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = L2CAP_MTU_SIZE; + +/* Defaults set by memset + cfg.flush_to_present = FALSE; + cfg.qos_present = FALSE; + cfg.fcr_present = FALSE; + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + cfg.fcs_present = FALSE; + cfg.fcs = N/A when fcs_present is FALSE; +*/ + L2CA_ConfigReq (p_mcb->lcid, &cfg); +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_cnf +** +** Description This function handles L2CA_ConfigCnf message from the +** L2CAP. If result is not success tell upper layer that +** start has not been accepted. If initiator send SABME +** on DLCI 0. T1 is still running. +** +*******************************************************************************/ +static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + RFCOMM_TRACE_EVENT2 ("rfc_mx_conf_cnf p_cfg:%08x res:%d ", p_cfg, (p_cfg) ? p_cfg->result : 0); + + if (p_cfg->result != L2CAP_CFG_OK) + { + if (p_mcb->is_initiator) + { + PORT_StartCnf (p_mcb, p_cfg->result); + L2CA_DisconnectReq (p_mcb->lcid); + } + rfc_release_multiplexer_channel (p_mcb); + return; + } + + p_mcb->local_cfg_sent = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->peer_cfg_rcvd) + { + if (p_mcb->is_initiator) + { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } + else + { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFC_T2_TIMEOUT); + } + } +} + + +/******************************************************************************* +** +** Function rfc_mx_conf_ind +** +** Description This function handles L2CA_ConfigInd message from the +** L2CAP. Send the L2CA_ConfigRsp message. +** +*******************************************************************************/ +static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg) +{ + /* Save peer L2CAP MTU if present */ + /* RFCOMM adds 3-4 bytes in the beginning and 1 bytes FCS */ + if (p_cfg->mtu_present) + p_mcb->peer_l2cap_mtu = p_cfg->mtu - RFCOMM_MIN_OFFSET - 1; + else + p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; + + p_cfg->mtu_present = FALSE; + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + p_cfg->result = L2CAP_CFG_OK; + + L2CA_ConfigRsp (p_mcb->lcid, p_cfg); + + p_mcb->peer_cfg_rcvd = TRUE; + if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->local_cfg_sent) + { + if (p_mcb->is_initiator) + { + p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA; + rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI); + rfc_timer_start (p_mcb, RFC_T1_TIMEOUT); + } + else + { + p_mcb->state = RFC_MX_STATE_WAIT_SABME; + rfc_timer_start (p_mcb, RFC_T2_TIMEOUT); + } + } +} diff --git a/stack/rfcomm/rfc_port_fsm.c b/stack/rfcomm/rfc_port_fsm.c new file mode 100644 index 0000000..d5335ad --- /dev/null +++ b/stack/rfcomm/rfc_port_fsm.c @@ -0,0 +1,915 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains state machine and action routines for a port of the + * RFCOMM unit + * + ******************************************************************************/ +#include +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "btm_api.h" +#include "btm_int.h" +#include "port_api.h" +#include "port_int.h" +#include "rfc_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 rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data); +static void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data); + +static void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf); + +static void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame); + + +/******************************************************************************* +** +** Function rfc_port_sm_execute +** +** Description This function sends port events through the state +** machine. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data) +{ + if (!p_port) + { + RFCOMM_TRACE_WARNING1 ("NULL port event %d", event); + return; + } + + switch (p_port->rfc.state) + { + case RFC_STATE_CLOSED: + rfc_port_sm_state_closed (p_port, event, p_data); + break; + + case RFC_STATE_SABME_WAIT_UA: + rfc_port_sm_sabme_wait_ua (p_port, event, p_data); + break; + + case RFC_STATE_ORIG_WAIT_SEC_CHECK: + rfc_port_sm_orig_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_TERM_WAIT_SEC_CHECK: + rfc_port_sm_term_wait_sec_check (p_port, event, p_data); + break; + + case RFC_STATE_OPENED: + rfc_port_sm_opened (p_port, event, p_data); + break; + + case RFC_STATE_DISC_WAIT_UA: + rfc_port_sm_disc_wait_ua (p_port, event, p_data); + break; + } +} + + +/******************************************************************************* +** +** Function rfc_port_sm_state_closed +** +** Description This function handles events when the port is in +** CLOSED state. This state exists when port is +** being initially established. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + p_port->rfc.state = RFC_STATE_ORIG_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, TRUE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_CLOSE: + break; + + case RFC_EVENT_CLEAR: + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + break; + + case RFC_EVENT_SABME: + /* make sure the multiplexer disconnect timer is not running (reconnect case) */ + rfc_timer_stop(p_port->rfc.p_mcb ); + + /* Open will be continued after security checks are passed */ + p_port->rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK; + btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, FALSE, + BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), + &rfc_sec_check_complete, p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + + RFCOMM_TRACE_WARNING1 ("Port state closed Event ignored %d", event); + return; +} + +/******************************************************************************* +** +** Function rfc_port_sm_sabme_wait_ua +** +** Description This function handles events when SABME on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + break; + + case RFC_EVENT_UA: + rfc_port_timer_stop (p_port); + p_port->rfc.state = RFC_STATE_OPENED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_SUCCESS); + return; + + case RFC_EVENT_DM: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + /* Continue to wait for the UA the SABME this side sent */ + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_TIMEOUT: + p_port->rfc.state = RFC_STATE_CLOSED; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state sabme_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_term_wait_sec_check +** +** Description This function handles events for the port in the +** WAIT_SEC_CHECK state. SABME has been received from the +** peer and Security Manager verifes BD_ADDR, before we can +** send ESTABLISH_IND to the Port entity +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) + { + /* Authentication/authorization failed. If link is still */ + /* up send DM and check if we need to start inactive timer */ + if (p_port->rfc.p_mcb) + { + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + port_rfc_closed (p_port, PORT_SEC_FAILED); + } + } + else + { + PORT_DlcEstablishInd (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu); + } + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_CLOSE: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR0 ("Port error state Term Wait Sec event Data"); + GKI_freebuf (p_data); + return; + + case RFC_EVENT_SABME: + /* Ignore SABME retransmission if client dares to do so */ + return; + + case RFC_EVENT_DISC: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_ESTABLISH_RSP: + if (*((UINT8 *)p_data) != RFCOMM_SUCCESS) + { + if (p_port->rfc.p_mcb) + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + } + else + { + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.state = RFC_STATE_OPENED; + } + return; + } + RFCOMM_TRACE_WARNING1 ("Port state term_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_orig_wait_sec_check +** +** Description This function handles events for the port in the +** ORIG_WAIT_SEC_CHECK state. RFCOMM is waiting for Security +** manager to finish before sending SABME to the peer +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_SEC_COMPLETE: + if (*((UINT8 *)p_data) != BTM_SUCCESS) + { + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, 0, RFCOMM_SECURITY_ERR); + rfc_port_closed (p_port); + return; + } + rfc_send_sabme (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_timer_start (p_port, RFC_PORT_T1_TIMEOUT); + p_port->rfc.state = RFC_STATE_SABME_WAIT_UA; + return; + + case RFC_EVENT_OPEN: + case RFC_EVENT_SABME: /* Peer should not use the same dlci */ + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + RFCOMM_TRACE_ERROR0 ("Port error state Orig Wait Sec event Data"); + GKI_freebuf (p_data); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state orig_wait_sec_check Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_opened +** +** Description This function handles events for the port in the OPENED +** state +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLOSE: + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci); + p_port->rfc.expected_rsp = 0; + p_port->rfc.state = RFC_STATE_DISC_WAIT_UA; + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + /* Send credits in the frame. Pass them in the layer specific member of the hdr. */ + /* There might be an initial case when we reduced rx_max and credit_rx is still */ + /* bigger. Make sure that we do not send 255 */ + if ((p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + && (((BT_HDR *)p_data)->len < p_port->peer_mtu) + && (!p_port->rx.user_fc) + && (p_port->credit_rx_max > p_port->credit_rx)) + { + ((BT_HDR *)p_data)->layer_specific = (UINT8) (p_port->credit_rx_max - p_port->credit_rx); + p_port->credit_rx = p_port->credit_rx_max; + } + else + { + ((BT_HDR *)p_data)->layer_specific = 0; + } + rfc_send_buf_uih (p_port->rfc.p_mcb, p_port->dlci, (BT_HDR *)p_data); + rfc_dec_credit (p_port); + return; + + case RFC_EVENT_UA: + return; + + case RFC_EVENT_SABME: + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_DM: + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DISC: + p_port->rfc.state = RFC_STATE_CLOSED; + rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); + if(p_port->rx.queue.count) + { + /* give a chance to upper stack to close port properly */ + RFCOMM_TRACE_DEBUG0("port queue is not empty"); + rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT); + } + else + PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); + return; + + case RFC_EVENT_UIH: + rfc_port_uplink_data (p_port, (BT_HDR *)p_data); + return; + + case RFC_EVENT_TIMEOUT: + Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + } + RFCOMM_TRACE_WARNING1 ("Port state opened Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_sm_disc_wait_ua +** +** Description This function handles events when DISC on the DLC was +** sent and SM is waiting for UA or DM. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data) +{ + switch (event) + { + case RFC_EVENT_OPEN: + case RFC_EVENT_ESTABLISH_RSP: + RFCOMM_TRACE_ERROR2 ("Port error state %d event %d", p_port->rfc.state, event); + return; + + case RFC_EVENT_CLEAR: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_DATA: + GKI_freebuf (p_data); + return; + + case RFC_EVENT_UA: + p_port->rfc.p_mcb->is_disc_initiator = TRUE; + /* Case falls through */ + + case RFC_EVENT_DM: + rfc_port_closed (p_port); + return; + + case RFC_EVENT_SABME: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_DISC: + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); + return; + + case RFC_EVENT_UIH: + GKI_freebuf (p_data); + rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); + return; + + case RFC_EVENT_TIMEOUT: + rfc_port_closed (p_port); + return; + } + + RFCOMM_TRACE_WARNING1 ("Port state disc_wait_ua Event ignored %d", event); +} + + +/******************************************************************************* +** +** Function rfc_port_uplink_data +** +** Description This function handles uplink information data frame. +** +*******************************************************************************/ +void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf) +{ + PORT_DataInd (p_port->rfc.p_mcb, p_port->dlci, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_pn +** +** Description This function handles DLC parameter negotiation frame. +** Record MTU and pass indication to the upper layer. +** +*******************************************************************************/ +void rfc_process_pn (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + UINT8 dlci = p_frame->dlci; + + if (is_command) + { + /* Ignore if Multiplexer is being shut down */ + if (p_mcb->state != RFC_MX_STATE_DISC_WAIT_UA) + { + PORT_ParNegInd (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); + } + else + { + rfc_send_dm(p_mcb, dlci, FALSE); + RFCOMM_TRACE_WARNING0("***** MX PN while disconnecting *****"); + } + + return; + } + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_PN; + + rfc_port_timer_stop (p_port); + + PORT_ParNegCnf (p_mcb, dlci, p_frame->u.pn.mtu, + p_frame->u.pn.conv_layer, p_frame->u.pn.k); +} + + +/******************************************************************************* +** +** Function rfc_process_rpn +** +** Description This function handles Remote DLC parameter negotiation +** command/response. Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rpn (tRFC_MCB *p_mcb, BOOLEAN is_command, + BOOLEAN is_request, MX_FRAME *p_frame) +{ + tPORT_STATE port_pars; + tPORT *p_port; + + if ((p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci)) == NULL) + { + /* This is the first command on the port */ + if (is_command) + { + + memset(&port_pars, 0, sizeof(tPORT_STATE)); + rfc_set_port_state(&port_pars, p_frame); + + PORT_PortNegInd(p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + } + return; + } + + if (is_command && is_request) + { + /* This is the special situation when peer just request local pars */ + port_pars = p_port->peer_port_pars; + rfc_send_rpn (p_mcb, p_frame->dlci, FALSE, &p_port->peer_port_pars, 0); + return; + } + + port_pars = p_port->peer_port_pars; + + rfc_set_port_state(&port_pars, p_frame); + + if (is_command) + { + PORT_PortNegInd (p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask); + return; + } + + /* If we are not awaiting response just ignore it */ + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if ((p_port == NULL) || !(p_port->rfc.expected_rsp & (RFC_RSP_RPN | RFC_RSP_RPN_REPLY))) + return; + + /* If we sent a request for port parameters to the peer he is replying with */ + /* mask 0. */ + rfc_port_timer_stop (p_port); + + if (p_port->rfc.expected_rsp & RFC_RSP_RPN_REPLY) + { + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN_REPLY; + + p_port->peer_port_pars = port_pars; + + if ((port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) + || (port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT))) + { + /* This is satisfactory port parameters. Set mask as it was Ok */ + p_frame->u.rpn.param_mask = RFCOMM_RPN_PM_MASK; + } + else + { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + } + else + p_port->rfc.expected_rsp &= ~RFC_RSP_RPN; + + /* Check if all suggested parameters were accepted */ + if (((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) + || ((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT)) == + (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT))) + { + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + return; + } + + /* If we were proposing RTR flow control try RTC flow control */ + /* If we were proposing RTC flow control try no flow control */ + /* otherwise drop the connection */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) + { + /* Current peer parameters are not good, try to fix them */ + p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT); + + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + + rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars, + RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + return; + } + + /* Other side does not support flow control */ + if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT)) + { + p_port->peer_port_pars.fc_type = RFCOMM_FC_OFF; + PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS); + } +} + + +/******************************************************************************* +** +** Function rfc_process_msc +** +** Description This function handles Modem Status Command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_msc (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT_CTRL pars; + tPORT *p_port; + UINT8 modem_signals = p_frame->u.msc.signals; + BOOLEAN new_peer_fc = FALSE; + + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + if (p_port == NULL) + return; + + pars.modem_signal = 0; + + if (modem_signals & RFCOMM_MSC_RTC) + pars.modem_signal |= MODEM_SIGNAL_DTRDSR; + + if (modem_signals & RFCOMM_MSC_RTR) + pars.modem_signal |= MODEM_SIGNAL_RTSCTS; + + if (modem_signals & RFCOMM_MSC_IC) + pars.modem_signal |= MODEM_SIGNAL_RI; + + if (modem_signals & RFCOMM_MSC_DV) + pars.modem_signal |= MODEM_SIGNAL_DCD; + + pars.fc = ((modem_signals & RFCOMM_MSC_FC) == RFCOMM_MSC_FC); + + pars.break_signal = (p_frame->u.msc.break_present) ? + p_frame->u.msc.break_duration : 0; + pars.discard_buffers = 0; + pars.break_signal_seq = RFCOMM_CTRL_BREAK_IN_SEQ; /* this is default */ + + /* Check if this command is passed only to indicate flow control */ + if (is_command) + { + rfc_send_msc (p_mcb, p_frame->dlci, FALSE, &pars); + + if (p_port->rfc.p_mcb->flow != PORT_FC_CREDIT) + { + /* Spec 1.1 indicates that only FC bit is used for flow control */ + p_port->peer_ctrl.fc = new_peer_fc = pars.fc; + + if (new_peer_fc != p_port->tx.peer_fc) + PORT_FlowInd (p_mcb, p_frame->dlci, (BOOLEAN)!new_peer_fc); + } + + PORT_ControlInd (p_mcb, p_frame->dlci, &pars); + + return; + } + + /* If we are not awaiting response just ignore it */ + if (!(p_port->rfc.expected_rsp & RFC_RSP_MSC)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_MSC; + + rfc_port_timer_stop (p_port); + + PORT_ControlCnf (p_port->rfc.p_mcb, p_port->dlci, &pars); +} + + +/******************************************************************************* +** +** Function rfc_process_rls +** +** Description This function handles Remote Line Status command. +** Pass command to the user. +** +*******************************************************************************/ +void rfc_process_rls (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) +{ + tPORT *p_port; + + if (is_command) + { + PORT_LineStatusInd (p_mcb, p_frame->dlci, p_frame->u.rls.line_status); + rfc_send_rls (p_mcb, p_frame->dlci, FALSE, p_frame->u.rls.line_status); + } + else + { + p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci); + + /* If we are not awaiting response just ignore it */ + if (!p_port || !(p_port->rfc.expected_rsp & RFC_RSP_RLS)) + return; + + p_port->rfc.expected_rsp &= ~RFC_RSP_RLS; + + rfc_port_timer_stop (p_port); + } +} + + +/******************************************************************************* +** +** Function rfc_process_nsc +** +** Description This function handles None Supported Command frame. +** +*******************************************************************************/ +void rfc_process_nsc (tRFC_MCB *p_mcb, MX_FRAME *p_frame) +{ +} + + +/******************************************************************************* +** +** Function rfc_process_test +** +** Description This function handles Test frame. If this is a command +** reply to it. Otherwise pass response to the user. +** +*******************************************************************************/ +void rfc_process_test_rsp (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + GKI_freebuf (p_buf); +} + + +/******************************************************************************* +** +** Function rfc_process_fcon +** +** Description This function handles FCON frame. The peer entity is able +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) + { + rfc_cb.rfc.peer_rx_disabled = FALSE; + + rfc_send_fcon (p_mcb, FALSE); + + if (!p_mcb->l2cap_congested) + PORT_FlowInd (p_mcb, 0, TRUE); + } +} + +/******************************************************************************* +** +** Function rfc_process_fcoff +** +** Description This function handles FCOFF frame. The peer entity is unable +** to receive new information +** +*******************************************************************************/ +void rfc_process_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + if (is_command) + { + rfc_cb.rfc.peer_rx_disabled = TRUE; + + if (!p_mcb->l2cap_congested) + PORT_FlowInd (p_mcb, 0, FALSE); + + rfc_send_fcoff (p_mcb, FALSE); + } +} + + +/******************************************************************************* +** +** Function rfc_process_l2cap_congestion +** +** Description This function handles L2CAP congestion messages +** +*******************************************************************************/ +void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested) +{ + p_mcb->l2cap_congested = is_congested; + + if (!is_congested) + { + rfc_check_send_cmd(p_mcb, NULL); + } + + if (!rfc_cb.rfc.peer_rx_disabled) + { + if (!is_congested) + PORT_FlowInd (p_mcb, 0, TRUE); + else + PORT_FlowInd (p_mcb, 0, FALSE); + } +} + +/******************************************************************************* +** +** Function rfc_set_port_pars +** +** Description This function sets the tPORT_STATE structure given a p_frame. +** +*******************************************************************************/ + +void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame) +{ + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_BIT_RATE) + port_pars->baud_rate = p_frame->u.rpn.baud_rate; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_DATA_BITS) + port_pars->byte_size = p_frame->u.rpn.byte_size; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_STOP_BITS) + port_pars->stop_bits = p_frame->u.rpn.stop_bits; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY) + port_pars->parity = p_frame->u.rpn.parity; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY_TYPE) + port_pars->parity_type = p_frame->u.rpn.parity_type; + if (p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_XONXOFF_ON_INPUT | + RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT | + RFCOMM_RPN_PM_RTR_ON_INPUT | + RFCOMM_RPN_PM_RTR_ON_OUTPUT | + RFCOMM_RPN_PM_RTC_ON_INPUT | + RFCOMM_RPN_PM_RTC_ON_OUTPUT)) + port_pars->fc_type = p_frame->u.rpn.fc_type; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XON_CHAR) + port_pars->xon_char = p_frame->u.rpn.xon_char; + if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XOFF_CHAR) + port_pars->xoff_char = p_frame->u.rpn.xoff_char; +} + diff --git a/stack/rfcomm/rfc_port_if.c b/stack/rfcomm/rfc_port_if.c new file mode 100644 index 0000000..083d9ef --- /dev/null +++ b/stack/rfcomm/rfc_port_if.c @@ -0,0 +1,339 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains functions callable by an application + * running on top of RFCOMM + * + *****************************************************************************/ + +#include +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" + + +#if RFC_DYNAMIC_MEMORY == FALSE +tRFC_CB rfc_cb; +#endif + +/******************************************************************************* +** +** Function RFCOMM_StartReq +** +** Description This function handles Start Request from the upper layer. +** If RFCOMM multiplexer channel can not be allocated +** send start not accepted confirmation. Otherwise dispatch +** start event to the state machine. +** +*******************************************************************************/ +void RFCOMM_StartReq (tRFC_MCB *p_mcb) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_REQ, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_StartRsp +** +** Description This function handles Start Response from the upper layer. +** Save upper layer handle and result of the Start Indication +** in the control block and dispatch event to the FSM. +** +*******************************************************************************/ +void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result) +{ + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishReq +** +** Description This function is called by the user app to establish +** connection with the specific dlci on a specific bd device. +** It will allocate RFCOMM connection control block if not +** allocated before and dispatch open event to the state +** machine. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + PORT_DlcEstablishCnf (p_mcb, dlci, 0, RFCOMM_ERROR); + return; + } + + rfc_port_sm_execute(p_port, RFC_EVENT_OPEN, NULL); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcEstablishRsp +** +** Description This function is called by the port emulation entity +** acks Establish Indication. +** +*******************************************************************************/ +void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_mcb->state != RFC_MX_STATE_CONNECTED) && (result == RFCOMM_SUCCESS)) + { + PORT_DlcReleaseInd (p_mcb, dlci); + return; + } + + rfc_port_sm_execute(p_port, RFC_EVENT_ESTABLISH_RSP, &result); +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegReq +** +** Description This function is called by the user app to start +** DLC parameter negotiation. Port emulation can send this +** request before actually establishing the DLC. In this +** case the function will allocate RFCOMM connection control +** block. +** +*******************************************************************************/ +void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + UINT8 flow; + UINT8 cl; + UINT8 k; + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + p_port->error = PORT_PAR_NEG_FAILED; + return; + } + + /* Negotiate the flow control mechanism. If flow control mechanism for */ + /* mux has not been set yet, use our default value. If it has been set, */ + /* use that value. */ + flow = (p_mcb->flow == PORT_FC_UNDEFINED) ? PORT_FC_DEFAULT : p_mcb->flow; + + /* Set convergence layer and number of credits (k) */ + if (flow == PORT_FC_CREDIT) + { + cl = RFCOMM_PN_CONV_LAYER_CBFC_I; + k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; + p_port->credit_rx = k; + } + else + { + cl = RFCOMM_PN_CONV_LAYER_TYPE_1; + k = 0; + } + + /* Send Parameter Negotiation Command UIH frame */ + p_port->rfc.expected_rsp |= RFC_RSP_PN; + + rfc_send_pn (p_mcb, dlci, TRUE, mtu, cl, k); + + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; +} + + +/******************************************************************************* +** +** Function RFCOMM_ParNegRsp +** +** Description This function is called by the user app to acknowledge +** DLC parameter negotiation. +** +*******************************************************************************/ +void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + return; + + /* Send Parameter Negotiation Response UIH frame */ + rfc_send_pn (p_mcb, dlci, FALSE, mtu, cl, k); +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegReq +** +** Description This function is called by the user app to start +** Remote Port parameter negotiation. Port emulation can +** send this request before actually establishing the DLC. +** In this case the function will allocate RFCOMM connection +** control block. +** +*******************************************************************************/ +void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + { + PORT_PortNegCnf (p_mcb, dlci, NULL, RFCOMM_ERROR); + return; + } + + /* Send Parameter Negotiation Command UIH frame */ + if (!p_pars) + p_port->rfc.expected_rsp |= RFC_RSP_RPN_REPLY; + else + p_port->rfc.expected_rsp |= RFC_RSP_RPN; + + rfc_send_rpn (p_mcb, dlci, TRUE, p_pars, RFCOMM_RPN_PM_MASK); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_PortNegRsp +** +** Description This function is called by the user app to acknowledge +** Port parameters negotiation. +** +*******************************************************************************/ +void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, + UINT16 param_mask) +{ + if (p_mcb->state != RFC_MX_STATE_CONNECTED) + return; + + rfc_send_rpn (p_mcb, dlci, FALSE, p_pars, param_mask); +} + + +/******************************************************************************* +** +** Function RFCOMM_ControlReq +** +** Description This function is called by the port entity to send control +** parameters to remote port emulation entity. +** +*******************************************************************************/ +void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->port_ctrl |= PORT_CTRL_REQ_SENT; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, p_pars); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_FlowReq +** +** Description This function is called by the port entity when flow +** control state has changed. Enable flag passed shows if +** port can accept more data. +** +*******************************************************************************/ +void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 enable) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->local_ctrl.fc = !enable; + + p_port->rfc.expected_rsp |= RFC_RSP_MSC; + + rfc_send_msc (p_mcb, dlci, TRUE, &p_port->local_ctrl); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ; + +} + + +/******************************************************************************* +** +** Function RFCOMM_LineStatusReq +** +** Description This function is called by the port entity when line +** status should be delivered to the peer. +** +*******************************************************************************/ +void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 status) +{ + tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); + + if ((p_port->state != PORT_STATE_OPENED) + || (p_port->rfc.state != RFC_STATE_OPENED)) + return; + + p_port->rfc.expected_rsp |= RFC_RSP_RLS; + + rfc_send_rls (p_mcb, dlci, TRUE, status); + rfc_port_timer_start (p_port, RFC_T2_TIMEOUT); +} + + +/******************************************************************************* +** +** Function RFCOMM_DlcReleaseReq +** +** Description This function is called by the PORT unit to close DLC +** +*******************************************************************************/ +void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_CLOSE, 0); +} + + +/******************************************************************************* +** +** Function RFCOMM_DataReq +** +** Description This function is called by the user app to send data buffer +** +*******************************************************************************/ +void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_DATA, p_buf); +} + + diff --git a/stack/rfcomm/rfc_ts_frames.c b/stack/rfcomm/rfc_ts_frames.c new file mode 100644 index 0000000..4690200 --- /dev/null +++ b/stack/rfcomm/rfc_ts_frames.c @@ -0,0 +1,908 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions to send TS 07.10 frames + * + ******************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "l2c_api.h" +#include "port_int.h" +#include "rfc_int.h" + +/******************************************************************************* +** +** Function rfc_send_sabme +** +** Description This function sends SABME frame. +** +*******************************************************************************/ +void rfc_send_sabme (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* SABME frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_SABME | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_SABME_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_ua +** +** Description This function sends UA frame. +** +*******************************************************************************/ +void rfc_send_ua (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* ua frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UA | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_UA_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_dm +** +** Description This function sends DM frame. +** +*******************************************************************************/ +void rfc_send_dm (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN pf) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DM frame, response, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DM | ((pf) ? RFCOMM_PF : 0); + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DM_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_disc +** +** Description This function sends DISC frame. +** +*******************************************************************************/ +void rfc_send_disc (tRFC_MCB *p_mcb, UINT8 dlci) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* DISC frame, command, PF = 1, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_DISC | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + + *p_data = RFCOMM_DISC_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci); + + p_buf->len = 4; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_buf_uih +** +** Description This function sends UIH frame. +** +*******************************************************************************/ +void rfc_send_buf_uih (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + UINT8 credits; + + p_buf->offset -= RFCOMM_CTRL_FRAME_LEN; + if (p_buf->len > 127) + p_buf->offset--; + + if (dlci) + credits = (UINT8)p_buf->layer_specific; + else + credits = 0; + + if (credits) + p_buf->offset--; + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* UIH frame, command, PF = 0, dlci */ + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | ((credits) ? RFCOMM_PF : 0); + if (p_buf->len <= 127) + { + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + p_buf->len += 3; + } + else + { + *p_data++ = (p_buf->len & 0x7f) << 1; + *p_data++ = p_buf->len >> RFCOMM_SHIFT_LENGTH2; + p_buf->len += 4; + } + + if (credits) + { + *p_data++ = credits; + p_buf->len++; + } + + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len++; + + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + if (dlci == RFCOMM_MX_DLCI) + { + rfc_check_send_cmd(p_mcb, p_buf); + } + else + { + + L2CA_DataWrite (p_mcb->lcid, p_buf); + } +} + + +/******************************************************************************* +** +** Function rfc_send_pn +** +** Description This function sends DLC Parameters Negotiation Frame. +** +*******************************************************************************/ +void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, UINT8 cl, UINT8 k) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_PN; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_PN_LEN << 1); + + *p_data++ = dlci; + *p_data++ = RFCOMM_PN_FRAM_TYPE_UIH | cl; + + /* It appeared that we need to reply with the same priority bits as we received. + ** We will use the fact that we reply in the same context so rx_frame can still be used. + */ + if (is_command) + *p_data++ = RFCOMM_PN_PRIORITY_0; + else + *p_data++ = rfc_cb.rfc.rx_frame.u.pn.priority; + + *p_data++ = RFCOMM_T1_DSEC; + *p_data++ = mtu & 0xFF; + *p_data++ = mtu >> 8; + *p_data++ = RFCOMM_N2; + *p_data = k; + + /* Total length is sizeof PN data + mx header 2 */ + p_buf->len = RFCOMM_MX_PN_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcon +** +** Description This function sends Flow Control On Command. +** +*******************************************************************************/ +void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCON; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCON_LEN << 1); + + /* Total length is sizeof FCON data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCON_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_fcoff +** +** Description This function sends Flow Control Off Command. +** +*******************************************************************************/ +void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCOFF; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_FCOFF_LEN << 1); + + /* Total length is sizeof FCOFF data + mx header 2 */ + p_buf->len = RFCOMM_MX_FCOFF_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_msc +** +** Description This function sends Modem Status Command Frame. +** +*******************************************************************************/ +void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_CTRL *p_pars) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 signals; + UINT8 break_duration; + UINT8 len; + + signals = p_pars->modem_signal; + break_duration = p_pars->break_signal; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + if (break_duration) + len = RFCOMM_MX_MSC_LEN_WITH_BREAK; + else + len = RFCOMM_MX_MSC_LEN_NO_BREAK; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_MSC; + *p_data++ = RFCOMM_EA | (len << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_EA | + ((p_pars->fc) ? RFCOMM_MSC_FC : 0) | + ((signals & MODEM_SIGNAL_DTRDSR) ? RFCOMM_MSC_RTC : 0) | + ((signals & MODEM_SIGNAL_RTSCTS) ? RFCOMM_MSC_RTR : 0) | + ((signals & MODEM_SIGNAL_RI) ? RFCOMM_MSC_IC : 0) | + ((signals & MODEM_SIGNAL_DCD) ? RFCOMM_MSC_DV : 0); + + if (break_duration) + { + *p_data++ = RFCOMM_EA | RFCOMM_MSC_BREAK_PRESENT_MASK | + (break_duration << RFCOMM_MSC_SHIFT_BREAK); + } + + /* Total length is sizeof MSC data + mx header 2 */ + p_buf->len = len + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rls +** +** Description This function sends Remote Line Status Command Frame. +** +*******************************************************************************/ +void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RLS; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RLS_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_RLS_ERROR | status; + + /* Total length is sizeof RLS data + mx header 2 */ + p_buf->len = RFCOMM_MX_RLS_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_nsc +** +** Description This function sends Non Supported Command Response. +** +*******************************************************************************/ +void rfc_send_nsc (tRFC_MCB *p_mcb) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(FALSE) | RFCOMM_MX_NSC; + *p_data++ = RFCOMM_EA | (RFCOMM_MX_NSC_LEN << 1); + + *p_data++ = rfc_cb.rfc.rx_frame.ea | + (rfc_cb.rfc.rx_frame.cr << RFCOMM_SHIFT_CR) | + rfc_cb.rfc.rx_frame.type; + + /* Total length is sizeof NSC data + mx header 2 */ + p_buf->len = RFCOMM_MX_NSC_LEN + 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_rpn +** +** Description This function sends Remote Port Negotiation Command +** +*******************************************************************************/ +void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, + tPORT_STATE *p_pars, UINT16 mask) +{ + BT_HDR *p_buf; + UINT8 *p_data; + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RPN; + + if (!p_pars) + { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_REQ_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + + p_buf->len = RFCOMM_MX_RPN_REQ_LEN + 2; + } + else + { + *p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_LEN << 1); + + *p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = p_pars->baud_rate; + *p_data++ = (p_pars->byte_size << RFCOMM_RPN_BITS_SHIFT) + | (p_pars->stop_bits << RFCOMM_RPN_STOP_BITS_SHIFT) + | (p_pars->parity << RFCOMM_RPN_PARITY_SHIFT) + | (p_pars->parity_type << RFCOMM_RPN_PARITY_TYPE_SHIFT); + *p_data++ = p_pars->fc_type; + *p_data++ = p_pars->xon_char; + *p_data++ = p_pars->xoff_char; + *p_data++ = (mask & 0xFF); + *p_data++ = (mask >> 8); + + /* Total length is sizeof RPN data + mx header 2 */ + p_buf->len = RFCOMM_MX_RPN_LEN + 2; + } + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_send_test +** +** Description This function sends Test frame. +** +*******************************************************************************/ +void rfc_send_test (tRFC_MCB *p_mcb, BOOLEAN is_command, BT_HDR *p_buf) +{ + UINT8 *p_data; + UINT16 xx; + UINT8 *p_src, *p_dest; + + /* Shift buffer to give space for header */ + if (p_buf->offset < (L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2)) + { + p_src = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len - 1; + p_dest = (UINT8 *) (p_buf + 1) + L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2 + p_buf->len - 1; + + for (xx = 0; xx < p_buf->len; xx++) + *p_dest-- = *p_src--; + + p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2; + } + + /* Adjust offset by number of bytes we are going to fill */ + p_buf->offset -= 2; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_TEST; + *p_data++ = RFCOMM_EA | (p_buf->len << 1); + + p_buf->len += 2; + + rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf); +} + +/******************************************************************************* +** +** Function rfc_send_credit +** +** Description This function sends a flow control credit in UIH frame. +** +*******************************************************************************/ +void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit) +{ + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE); + + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (RFCOMM_CMD_POOL_ID)) == NULL) + return; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + + *p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI); + *p_data++ = RFCOMM_UIH | RFCOMM_PF; + *p_data++ = RFCOMM_EA | 0; + *p_data++ = credit; + *p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci); + + p_buf->len = 5; + + rfc_check_send_cmd(p_mcb, p_buf); +} + + +/******************************************************************************* +** +** Function rfc_parse_data +** +** Description This function processes data packet received from L2CAP +** +*******************************************************************************/ +UINT8 rfc_parse_data (tRFC_MCB *p_mcb, MX_FRAME *p_frame, BT_HDR *p_buf) +{ + UINT8 ead, eal, fcs; + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 *p_start = p_data; + UINT16 len; + + if (p_buf->len < RFCOMM_CTRL_FRAME_LEN) + { + RFCOMM_TRACE_ERROR1 ("Bad Length1: %d", p_buf->len); + return (RFC_EVENT_BAD_FRAME); + } + + RFCOMM_PARSE_CTRL_FIELD (ead, p_frame->cr, p_frame->dlci, p_data); + if( !ead ) + { + RFCOMM_TRACE_ERROR0 ("Bad Address(EA must be 1)"); + return (RFC_EVENT_BAD_FRAME); + } + RFCOMM_PARSE_TYPE_FIELD (p_frame->type, p_frame->pf, p_data); + RFCOMM_PARSE_LEN_FIELD (eal, len, p_data); + + p_buf->len -= (3 + !ead + !eal + 1); /* Additional 1 for FCS */ + p_buf->offset += (3 + !ead + !eal); + + /* handle credit if credit based flow control */ + if ((p_mcb->flow == PORT_FC_CREDIT) && (p_frame->type == RFCOMM_UIH) && + (p_frame->dlci != RFCOMM_MX_DLCI) && (p_frame->pf == 1)) + { + p_frame->credit = *p_data++; + p_buf->len--; + p_buf->offset++; + } + else + p_frame->credit = 0; + + if (p_buf->len != len) + { + RFCOMM_TRACE_ERROR2 ("Bad Length2 %d %d", p_buf->len, len); + return (RFC_EVENT_BAD_FRAME); + } + + fcs = *(p_data + len); + + /* All control frames that we are sending are sent with P=1, expect */ + /* reply with F=1 */ + /* According to TS 07.10 spec ivalid frames are discarded without */ + /* notification to the sender */ + switch (p_frame->type) + { + case RFCOMM_SABME: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad SABME"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_SABME); + + case RFCOMM_UA: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad UA"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_UA); + + case RFCOMM_DM: + if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr) + || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad DM"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_DM); + + case RFCOMM_DISC: + if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr) + || !p_frame->pf || len || !RFCOMM_VALID_DLCI(p_frame->dlci) + || !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad DISC"); + return (RFC_EVENT_BAD_FRAME); + } + else + return (RFC_EVENT_DISC); + + case RFCOMM_UIH: + if (!RFCOMM_VALID_DLCI(p_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad UIH - invalid DLCI"); + return (RFC_EVENT_BAD_FRAME); + } + else if (!rfc_check_fcs (2, p_start, fcs)) + { + RFCOMM_TRACE_ERROR0 ("Bad UIH - FCS"); + return (RFC_EVENT_BAD_FRAME); + } + else if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)) + { + /* we assume that this is ok to allow bad implementations to work */ + RFCOMM_TRACE_ERROR0 ("Bad UIH - response"); + return (RFC_EVENT_UIH); + } + else + return (RFC_EVENT_UIH); + } + + return (RFC_EVENT_BAD_FRAME); +} + + +/******************************************************************************* +** +** Function rfc_process_mx_message +** +** Description This function processes UIH frames received on the +** multiplexer control channel. +** +*******************************************************************************/ +void rfc_process_mx_message (tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; + MX_FRAME *p_rx_frame = &rfc_cb.rfc.rx_frame; + UINT16 length = p_buf->len; + UINT8 ea, cr, mx_len; + BOOLEAN is_command; + + p_rx_frame->ea = *p_data & RFCOMM_EA; + p_rx_frame->cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->type = *p_data++ & ~(RFCOMM_CR_MASK | RFCOMM_EA_MASK); + + if (!p_rx_frame->ea || !length) + { + RFCOMM_TRACE_ERROR2 ("Illegal MX Frame ea:%d len:%d", p_rx_frame->ea, length); + GKI_freebuf (p_buf); + return; + } + + length--; + + is_command = p_rx_frame->cr; + + ea = *p_data & RFCOMM_EA; + + mx_len = *p_data++ >> RFCOMM_SHIFT_LENGTH1; + length--; + + if (!ea) + { + mx_len += *p_data++ << RFCOMM_SHIFT_LENGTH2; + length --; + } + + if (mx_len != length) + { + RFCOMM_TRACE_ERROR0 ("Bad MX frame"); + GKI_freebuf (p_buf); + return; + } + + switch (p_rx_frame->type) + { + case RFCOMM_MX_PN: + if (length != RFCOMM_MX_PN_LEN) + break; + + p_rx_frame->dlci = *p_data++ & RFCOMM_PN_DLCI_MASK; + p_rx_frame->u.pn.frame_type = *p_data & RFCOMM_PN_FRAME_TYPE_MASK; + p_rx_frame->u.pn.conv_layer = *p_data++ & RFCOMM_PN_CONV_LAYER_MASK; + p_rx_frame->u.pn.priority = *p_data++ & RFCOMM_PN_PRIORITY_MASK; + p_rx_frame->u.pn.t1 = *p_data++; + p_rx_frame->u.pn.mtu = *p_data + (*(p_data + 1) << 8); + p_data += 2; + p_rx_frame->u.pn.n2 = *p_data++; + p_rx_frame->u.pn.k = *p_data++ & RFCOMM_PN_K_MASK; + + if (!p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci) + || (p_rx_frame->u.pn.mtu < RFCOMM_MIN_MTU) + || (p_rx_frame->u.pn.mtu > RFCOMM_MAX_MTU)) + { + RFCOMM_TRACE_ERROR0 ("Bad PN frame"); + break; + } + + GKI_freebuf (p_buf); + + rfc_process_pn (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_TEST: + if (!length) + break; + + p_rx_frame->u.test.p_data = p_data; + p_rx_frame->u.test.data_len = length; + + p_buf->offset += 2; + p_buf->len -= 2; + + if (is_command) + rfc_send_test (p_mcb, FALSE, p_buf); + else + rfc_process_test_rsp (p_mcb, p_buf); + return; + + case RFCOMM_MX_FCON: + if (length != RFCOMM_MX_FCON_LEN) + break; + + GKI_freebuf (p_buf); + + rfc_process_fcon (p_mcb, is_command); + return; + + case RFCOMM_MX_FCOFF: + if (length != RFCOMM_MX_FCOFF_LEN) + break; + + GKI_freebuf (p_buf); + + rfc_process_fcoff (p_mcb, is_command); + return; + + case RFCOMM_MX_MSC: + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad MSC frame"); + break; + } + + p_rx_frame->u.msc.signals = *p_data++; + + if (mx_len == RFCOMM_MX_MSC_LEN_WITH_BREAK) + { + p_rx_frame->u.msc.break_present = *p_data & RFCOMM_MSC_BREAK_PRESENT_MASK; + p_rx_frame->u.msc.break_duration = (*p_data & RFCOMM_MSC_BREAK_MASK) >> RFCOMM_MSC_SHIFT_BREAK; + } + else + { + p_rx_frame->u.msc.break_present = FALSE; + p_rx_frame->u.msc.break_duration = 0; + } + GKI_freebuf (p_buf); + + rfc_process_msc (p_mcb, is_command, p_rx_frame); + return; + + case RFCOMM_MX_NSC: + if ((length != RFCOMM_MX_NSC_LEN) || !is_command) + break; + + p_rx_frame->u.nsc.ea = *p_data & RFCOMM_EA; + p_rx_frame->u.nsc.cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->u.nsc.type = *p_data++ >> RFCOMM_SHIFT_DLCI; + + GKI_freebuf (p_buf); + + rfc_process_nsc (p_mcb, p_rx_frame); + return; + + case RFCOMM_MX_RPN: + if ((length != RFCOMM_MX_RPN_REQ_LEN) && (length != RFCOMM_MX_RPN_LEN)) + break; + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad RPN frame"); + break; + } + + p_rx_frame->u.rpn.is_request = (length == RFCOMM_MX_RPN_REQ_LEN); + + if (!p_rx_frame->u.rpn.is_request) + { + p_rx_frame->u.rpn.baud_rate = *p_data++; + p_rx_frame->u.rpn.byte_size = (*p_data >> RFCOMM_RPN_BITS_SHIFT) & RFCOMM_RPN_BITS_MASK; + p_rx_frame->u.rpn.stop_bits = (*p_data >> RFCOMM_RPN_STOP_BITS_SHIFT) & RFCOMM_RPN_STOP_BITS_MASK; + p_rx_frame->u.rpn.parity = (*p_data >> RFCOMM_RPN_PARITY_SHIFT) & RFCOMM_RPN_PARITY_MASK; + p_rx_frame->u.rpn.parity_type = (*p_data++ >> RFCOMM_RPN_PARITY_TYPE_SHIFT) & RFCOMM_RPN_PARITY_TYPE_MASK; + + p_rx_frame->u.rpn.fc_type = *p_data++ & RFCOMM_FC_MASK; + p_rx_frame->u.rpn.xon_char = *p_data++; + p_rx_frame->u.rpn.xoff_char = *p_data++; + p_rx_frame->u.rpn.param_mask = (*p_data + (*(p_data + 1) << 8)) & RFCOMM_RPN_PM_MASK; + } + GKI_freebuf (p_buf); + + rfc_process_rpn (p_mcb, is_command, p_rx_frame->u.rpn.is_request, p_rx_frame); + return; + + case RFCOMM_MX_RLS: + if (length != RFCOMM_MX_RLS_LEN) + break; + + ea = *p_data & RFCOMM_EA; + cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR; + + p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI; + p_rx_frame->u.rls.line_status = (*p_data & ~0x01); + + if (!ea || !cr || !p_rx_frame->dlci + || !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) + { + RFCOMM_TRACE_ERROR0 ("Bad RPN frame"); + break; + } + + GKI_freebuf (p_buf); + + rfc_process_rls (p_mcb, is_command, p_rx_frame); + return; + } + + GKI_freebuf (p_buf); + + if (is_command) + rfc_send_nsc (p_mcb); +} + diff --git a/stack/rfcomm/rfc_utils.c b/stack/rfcomm/rfc_utils.c new file mode 100644 index 0000000..49405ac --- /dev/null +++ b/stack/rfcomm/rfc_utils.c @@ -0,0 +1,467 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains collection of utility functions used the RFCOMM unit + * + *****************************************************************************/ + +#include "bt_target.h" +#include "gki.h" + +#include "btm_api.h" +#include "btm_int.h" +#include "rfcdefs.h" +#include "port_api.h" +#include "port_ext.h" +#include "port_int.h" +#include "rfc_int.h" +#include "btu.h" + +#include + +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description Reversed CRC Table , 8-bit, poly=0x07 +** (GSM 07.10 TS 101 369 V6.3.0) +*******************************************************************************/ +static const UINT8 rfc_crctable[] = +{ + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + + +/******************************************************************************* +** +** Function rfc_calc_fcs +** +** Description This function calculate FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** +*******************************************************************************/ +UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p) +{ + UINT8 fcs = 0xFF; + + while (len--) + { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + return (0xFF - fcs); +} + + +/******************************************************************************* +** +** Function rfc_check_fcs +** +** Description This function checks FCS for the RFCOMM frame +** (GSM 07.10 TS 101 369 V6.3.0) +** +** Input len - number of bytes in the message +** p - points to message +** received_fcs - received FCS +** +*******************************************************************************/ +BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs) +{ + UINT8 fcs = 0xFF; + + while (len--) + { + fcs = rfc_crctable[fcs ^ *p++]; + } + + /* Ones compliment */ + fcs = rfc_crctable[fcs ^ received_fcs]; + + /*0xCF is the reversed order of 11110011.*/ + return (fcs == 0xCF); +} + + +/******************************************************************************* +** +** Function rfc_alloc_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator) +{ + int i, j; + tRFC_MCB *p_mcb = NULL; + + for (i = 0; i < MAX_BD_CONNECTIONS; i++) + { + if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE) + && (!memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN))) + { + /* Multiplexer channel found do not change anything */ + /* If there was an inactivity timer running stop it now */ + if (rfc_cb.port.rfc_mcb[i].state == RFC_MX_STATE_CONNECTED) + rfc_timer_stop (&rfc_cb.port.rfc_mcb[i]); + return (&rfc_cb.port.rfc_mcb[i]); + } + } + + /* connection with bd_addr does not exist */ + for (i = 0, j = rfc_cb.rfc.last_mux + 1; i < MAX_BD_CONNECTIONS; i++, j++) + { + if (j >= MAX_BD_CONNECTIONS) + j = 0; + + p_mcb = &rfc_cb.port.rfc_mcb[j]; + if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) + { + /* New multiplexer control block */ + memset (p_mcb, 0, sizeof (tRFC_MCB)); + memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN); + + GKI_init_q(&p_mcb->cmd_q); + + p_mcb->is_initiator = is_initiator; + + rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); + + rfc_cb.rfc.last_mux = (UINT8) j; + return (p_mcb); + } + } + return (NULL); +} + + +/******************************************************************************* +** +** Function rfc_release_multiplexer_channel +** +** Description This function returns existing or new control block for +** the BD_ADDR. +** +*******************************************************************************/ +void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb) +{ + void *p_buf; + + rfc_timer_stop (p_mcb); + + while ((p_buf = GKI_dequeue(&p_mcb->cmd_q)) != NULL) + GKI_freebuf(p_buf); + + memset (p_mcb, 0, sizeof (tRFC_MCB)); + p_mcb->state = RFC_MX_STATE_IDLE; +} + + +/******************************************************************************* +** +** Function rfc_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_timer_start (tRFC_MCB *p_mcb, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_mcb->tle; + + RFCOMM_TRACE_EVENT1 ("rfc_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_mcb; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout); +} + + +/******************************************************************************* +** +** Function rfc_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_timer_stop (tRFC_MCB *p_mcb) +{ + RFCOMM_TRACE_EVENT0 ("rfc_timer_stop"); + + btu_stop_timer (&p_mcb->tle); +} + + +/******************************************************************************* +** +** Function rfc_port_timer_start +** +** Description Start RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_start (tPORT *p_port, UINT16 timeout) +{ + TIMER_LIST_ENT *p_tle = &p_port->rfc.tle; + + RFCOMM_TRACE_EVENT1 ("rfc_port_timer_start - timeout:%d", timeout); + + p_tle->param = (UINT32)p_port; + + btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout); +} + + +/******************************************************************************* +** +** Function rfc_port_timer_stop +** +** Description Stop RFC Timer +** +*******************************************************************************/ +void rfc_port_timer_stop (tPORT *p_port) +{ + RFCOMM_TRACE_EVENT0 ("rfc_port_timer_stop"); + + btu_stop_timer (&p_port->rfc.tle); +} + + +/******************************************************************************* +** +** Function rfc_check_mcb_active +** +** Description Check if there are any opened ports on the MCB if not +** start MCB Inact timer. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_mcb_active (tRFC_MCB *p_mcb) +{ + UINT16 i; + + for (i = 0; i < RFCOMM_MAX_DLCI; i++) + { + if (p_mcb->port_inx[i] != 0) + { + p_mcb->is_disc_initiator = FALSE; + return; + } + } + /* The last port was DISCed. On the client side start disconnecting Mx */ + /* On the server side start inactivity timer */ + if (p_mcb->is_disc_initiator) + { + p_mcb->is_disc_initiator = FALSE; + rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CLOSE_REQ, NULL); + } + else + rfc_timer_start (p_mcb, RFC_MCB_RELEASE_INACT_TIMER); +} + + +/******************************************************************************* +** +** Function rfcomm_process_timeout +** +** Description The function called every second to check RFCOMM timers +** +** Returns void +** +*******************************************************************************/ +void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle) +{ + switch (p_tle->event) + { + case BTU_TTYPE_RFCOMM_MFC: + rfc_mx_sm_execute ((tRFC_MCB *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + case BTU_TTYPE_RFCOMM_PORT: + rfc_port_sm_execute ((tPORT *)p_tle->param, RFC_EVENT_TIMEOUT, NULL); + break; + + default: + break; + } +} + + +/******************************************************************************* +** +** Function rfc_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +void rfc_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tPORT *p_port = (tPORT *)p_ref_data; + + /* Verify that PORT is still waiting for Security to complete */ + if (!p_port->in_use + || ((p_port->rfc.state != RFC_STATE_ORIG_WAIT_SEC_CHECK) + && (p_port->rfc.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) + return; + + rfc_port_sm_execute ((tPORT *)p_ref_data, RFC_EVENT_SEC_COMPLETE, &res); +} + + +/******************************************************************************* +** +** Function rfc_port_closed +** +** Description The function is called when port is released based on the +** event received from the lower layer, typically L2CAP +** connection down, DISC, or DM frame. +** +** Returns void +** +*******************************************************************************/ +void rfc_port_closed (tPORT *p_port) +{ + tRFC_MCB *p_mcb = p_port->rfc.p_mcb; + + RFCOMM_TRACE_DEBUG0 ("rfc_port_closed"); + + rfc_port_timer_stop (p_port); + + p_port->rfc.state = RFC_STATE_CLOSED; + + /* If multiplexer channel was up mark it as down */ + if (p_mcb) + { + p_mcb->port_inx[p_port->dlci] = 0; + + /* If there are no more ports opened on this MCB release it */ + rfc_check_mcb_active (p_mcb); + } + + /* Notify port that RFC connection is gone */ + port_rfc_closed (p_port, PORT_CLOSED); +} + +/******************************************************************************* +** +** Function rfc_inc_credit +** +** Description The function is called when a credit is received in a UIH +** frame. It increments the TX credit count, and if data +** flow had halted, it restarts it. +** +** Returns void +** +*******************************************************************************/ +void rfc_inc_credit (tPORT *p_port, UINT8 credit) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + p_port->credit_tx += credit; + + RFCOMM_TRACE_EVENT1 ("rfc_inc_credit:%d", p_port->credit_tx); + + if (p_port->tx.peer_fc == TRUE) + PORT_FlowInd(p_port->rfc.p_mcb, p_port->dlci, TRUE); + } +} + +/******************************************************************************* +** +** Function rfc_dec_credit +** +** Description The function is called when a UIH frame of user data is +** sent. It decrements the credit count. If credit count +** Reaches zero, peer_fc is set. +** +** Returns void +** +*******************************************************************************/ +void rfc_dec_credit (tPORT *p_port) +{ + if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) + { + if (p_port->credit_tx > 0) + p_port->credit_tx--; + + if (p_port->credit_tx == 0) + p_port->tx.peer_fc = TRUE; + } +} + + +/******************************************************************************* +** +** Function rfc_check_send_cmd +** +** Description This function is called to send an RFCOMM command message +** or to handle the RFCOMM command message queue. +** +** Returns void +** +*******************************************************************************/ +void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf) +{ + BT_HDR *p; + + /* if passed a buffer queue it */ + if (p_buf != NULL) + { + GKI_enqueue(&p_mcb->cmd_q, p_buf); + } + + /* handle queue if L2CAP not congested */ + while (p_mcb->l2cap_congested == FALSE) + { + if ((p = (BT_HDR *) GKI_dequeue(&p_mcb->cmd_q)) == NULL) + { + break; + } + + + L2CA_DataWrite (p_mcb->lcid, p); + } +} + + diff --git a/stack/sdp/sdp_api.c b/stack/sdp/sdp_api.c new file mode 100644 index 0000000..4899aa8 --- /dev/null +++ b/stack/sdp/sdp_api.c @@ -0,0 +1,1475 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains SDP interface functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" +#include "gki.h" +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "sdp_api.h" +#include "sdpint.h" +#include "btu.h" + +#include +#define info(fmt, ...) LOGI ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define debug(fmt, ...) LOGD ("%s: " fmt,__FUNCTION__, ## __VA_ARGS__) +#define error(fmt, ...) LOGE ("## ERROR : %s: " fmt "##",__FUNCTION__, ## __VA_ARGS__) +#define asrt(s) if(!(s)) LOGE ("## %s assert %s failed at line:%d ##",__FUNCTION__, #s, __LINE__) + + +/********************************************************************** +** C L I E N T F U N C T I O N P R O T O T Y P E S * +***********************************************************************/ + +/******************************************************************************* +** +** Function SDP_InitDiscoveryDb +** +** Description This function is called to initialize a discovery database. +** +** Parameters: p_db - (input) address of an area of memory where the +** discovery database is managed. +** len - (input) size (in bytes) of the memory +** NOTE: This must be larger than sizeof(tSDP_DISCOVERY_DB) +** num_uuid - (input) number of UUID filters applied +** p_uuid_list - (input) list of UUID filters +** num_attr - (input) number of attribute filters applied +** p_attr_list - (input) list of attribute filters +** +** +** Returns BOOLEAN +** TRUE if successful +** FALSE if one or more parameters are bad +** +*******************************************************************************/ +BOOLEAN SDP_InitDiscoveryDb (tSDP_DISCOVERY_DB *p_db, UINT32 len, UINT16 num_uuid, + tSDP_UUID *p_uuid_list, UINT16 num_attr, UINT16 *p_attr_list) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 xx; + + /* verify the parameters */ + if (p_db == NULL || (sizeof (tSDP_DISCOVERY_DB) > len) || + num_attr > SDP_MAX_ATTR_FILTERS || num_uuid > SDP_MAX_UUID_FILTERS) + { + SDP_TRACE_ERROR4("SDP_InitDiscoveryDb Illegal param: p_db 0x%x, len %d, num_uuid %d, num_attr %d", + (UINT32)p_db, len, num_uuid, num_attr); + + return(FALSE); + } + + memset (p_db, 0, (size_t)len); + + p_db->mem_size = len - sizeof (tSDP_DISCOVERY_DB); + p_db->mem_free = p_db->mem_size; + p_db->p_first_rec = NULL; + p_db->p_free_mem = (UINT8 *)(p_db + 1); + + for (xx = 0; xx < num_uuid; xx++) + p_db->uuid_filters[xx] = *p_uuid_list++; + + p_db->num_uuid_filters = num_uuid; + + for (xx = 0; xx < num_attr; xx++) + p_db->attr_filters[xx] = *p_attr_list++; + + /* sort attributes */ + sdpu_sort_attr_list( num_attr, p_db ); + + p_db->num_attr_filters = num_attr; +#endif + return(TRUE); +} + + + +/******************************************************************************* +** +** Function SDP_CancelServiceSearch +** +** Description This function cancels an active query to an SDP server. +** +** Returns TRUE if discovery cancelled, FALSE if a matching activity is not found. +** +*******************************************************************************/ +BOOLEAN SDP_CancelServiceSearch (tSDP_DISCOVERY_DB *p_db) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb = sdpu_find_ccb_by_db (p_db); + if (!p_ccb) + return(FALSE); + + sdp_disconnect (p_ccb, SDP_CANCEL); + p_ccb->disc_state = SDP_DISC_WAIT_CANCEL; +#endif + return(TRUE); +} + + + +/******************************************************************************* +** +** Function SDP_ServiceSearchRequest +** +** Description This function queries an SDP server for information. +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) + return(FALSE); + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb = p_cb; + + return(TRUE); +#else + return(FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function. +** (This is for Unplug Testing) +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB *p_cb) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) + return(FALSE); + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb = p_cb; + + p_ccb->is_attr_search = TRUE; + + return(TRUE); +#else + return(FALSE); +#endif +} +/******************************************************************************* +** +** Function SDP_ServiceSearchAttributeRequest2 +** +** Description This function queries an SDP server for information. +** +** The difference between this API function and the function +** SDP_ServiceSearchRequest is that this one does a +** combined ServiceSearchAttributeRequest SDP function. +** (This is for Unplug Testing) +** +** Returns TRUE if discovery started, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_ServiceSearchAttributeRequest2 (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db, + tSDP_DISC_CMPL_CB2 *p_cb2, void * user_data) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) + return(FALSE); + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = p_db; + p_ccb->p_cb2 = p_cb2; + + p_ccb->is_attr_search = TRUE; + p_ccb->user_data = user_data; + + return(TRUE); +#else + return(FALSE); +#endif +} + +#if SDP_CLIENT_ENABLED == TRUE +void SDP_SetIdleTimeout (BD_ADDR addr, UINT16 timeout) +{ +} +#endif + +/******************************************************************************* +** +** Function SDP_FindAttributeInDb +** +** Description This function queries an SDP database for a specific attribute. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to matching record, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindAttributeInDb (tSDP_DISCOVERY_DB *p_db, UINT16 attr_id, + tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr; + + /* Must have a valid database */ + if (p_db == NULL) + return(NULL); + + if (!p_start_rec) + p_rec = p_db->p_first_rec; + else + p_rec = p_start_rec->p_next_rec; + + while (p_rec) + { + p_attr = p_rec->p_first_attr; + while (p_attr) + { + if (p_attr->attr_id == attr_id) + return(p_rec); + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching attribute found */ + return(NULL); +} + + +/******************************************************************************* +** +** Function SDP_FindAttributeInRec +** +** Description This function searches an SDP discovery record for a specific +** attribute. +** +** Returns Pointer to matching attribute entry, or NULL +** +*******************************************************************************/ +tSDP_DISC_ATTR *SDP_FindAttributeInRec (tSDP_DISC_REC *p_rec, UINT16 attr_id) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr; + + p_attr = p_rec->p_first_attr; + while (p_attr) + { + if (p_attr->attr_id == attr_id) + return(p_attr); + + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no matching attribute found */ + return(NULL); +} + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec +** +** Description This function is called to read the service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID * p_uuid) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + p_attr = p_rec->p_first_attr; + + while (p_attr) + { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + { + /* only support 16 bits UUID for now */ + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) + { + p_uuid->len = 2; + p_uuid->uu.uuid16 = p_sattr->attr_value.v.u16; + } + return(TRUE); + } + + /* Checking for Toyota G Block Car Kit: + ** This car kit puts an extra data element sequence + ** where the UUID is suppose to be!!! + */ + else + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) + { + /* Look through data element sequence until no more UUIDs */ + for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) + { + /* Increment past this to see if the next attribut is UUID */ + if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE) + /* only support 16 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)) + { + p_uuid->len = 2; + p_uuid->uu.uuid16 = p_extra_sattr->attr_value.v.u16; + return(TRUE); + } + } + } + } + } + break; + } + else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) + { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + /* only support 16 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)) + { + p_uuid->len = 2; + p_uuid->uu.uuid16 = p_attr->attr_value.v.u16; + return(TRUE); + } + } + p_attr = p_attr->p_next_attr; + } + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInRec_128bit +** +** Description This function is called to read the 128-bit service UUID within a record +** if there is any. +** +** Parameters: p_rec - pointer to a SDP record. +** p_uuid - output parameter to save the UUID found. +** +** Returns TRUE if found, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN SDP_FindServiceUUIDInRec_128bit(tSDP_DISC_REC *p_rec, tBT_UUID * p_uuid) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + p_attr = p_rec->p_first_attr; + + while (p_attr) + { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + { + /* only support 128 bits UUID for now */ + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16) + { + p_uuid->len = 16; + memcpy(p_uuid->uu.uuid128, p_sattr->attr_value.v.array, MAX_UUID_SIZE); + } + return(TRUE); + } + } + break; + } + else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) + { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + /* only support 128 bits UUID for now */ + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) + { + p_uuid->len = 16; + memcpy(p_uuid->uu.uuid128, p_sattr->attr_value.v.array, MAX_UUID_SIZE); + return(TRUE); + } + } + p_attr = p_attr->p_next_attr; + } + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function SDP_FindServiceInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, UINT16 service_uuid, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + /* Must have a valid database */ + if (p_db == NULL) + return(NULL); + + if (!p_start_rec) + p_rec = p_db->p_first_rec; + else + p_rec = p_start_rec->p_next_rec; + + while (p_rec) + { + p_attr = p_rec->p_first_attr; + while (p_attr) + { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) ) { + printf("SDP_FindServiceInDb - p_sattr value = 0x%x serviceuuid = 0x%x\r\n", p_sattr->attr_value.v.u16, service_uuid); + if(service_uuid == UUID_SERVCLASS_HDP_PROFILE) + { + if( (p_sattr->attr_value.v.u16==UUID_SERVCLASS_HDP_SOURCE) || ( p_sattr->attr_value.v.u16==UUID_SERVCLASS_HDP_SOURCE)) + { + printf("SDP_FindServiceInDb found HDP source or sink\n" ); + return (p_rec); + } + } + + } + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) + /* for a specific uuid, or any one */ + && ((p_sattr->attr_value.v.u16 == service_uuid) || service_uuid == 0)) + { + return(p_rec); + } + + /* Checking for Toyota G Block Car Kit: + ** This car kit puts an extra data element sequence + ** where the UUID is suppose to be!!! + */ + else + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) + { + /* Look through data element sequence until no more UUIDs */ + for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) + { + /* Increment past this to see if the next attribut is UUID */ + if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2) + /* for a specific uuid, or any one */ + && ((p_extra_sattr->attr_value.v.u16 == service_uuid) || (service_uuid == 0))) + { + return(p_rec); + } + } + } + } + } + break; + } + else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) + { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2)) + { + printf("SDP_FindServiceInDb - p_attr value = 0x%x serviceuuid= 0x%x \r\n", p_attr->attr_value.v.u16, service_uuid); + } + + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 2) + /* find a specific UUID or anyone */ + && ((p_attr->attr_value.v.u16 == service_uuid) || service_uuid == 0)) + return(p_rec); + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching UUID found */ + return(NULL); +} + +/******************************************************************************* +** +** Function SDP_FindServiceInDb_128bit +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** This function is kept separate from SDP_FindServiceInDb since +** that API is expected to return only 16-bit UUIDs +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceInDb_128bit(tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr, *p_extra_sattr; + + /* Must have a valid database */ + if (p_db == NULL) + return(NULL); + + if (!p_start_rec) + p_rec = p_db->p_first_rec; + else + p_rec = p_start_rec->p_next_rec; + + while (p_rec) + { + p_attr = p_rec->p_first_attr; + while (p_attr) + { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 16)) + { + return(p_rec); + } + } + break; + } + else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) + { + if ((SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_attr->attr_len_type) == 16)) + return(p_rec); + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif + /* If here, no matching UUID found */ + return(NULL); +} + + +/******************************************************************************* +** +** Function SDP_FindServiceUUIDInDb +** +** Description This function queries an SDP database for a specific service. +** If the p_start_rec pointer is NULL, it looks from the beginning +** of the database, else it continues from the next record after +** p_start_rec. +** +** NOTE the only difference between this function and the previous function +** "SDP_FindServiceInDb()" is that this function takes a tBT_UUID input +** +** Returns Pointer to record containing service class, or NULL +** +*******************************************************************************/ +tSDP_DISC_REC *SDP_FindServiceUUIDInDb (tSDP_DISCOVERY_DB *p_db, tBT_UUID *p_uuid, tSDP_DISC_REC *p_start_rec) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_REC *p_rec; + tSDP_DISC_ATTR *p_attr, *p_sattr; + + /* Must have a valid database */ + if (p_db == NULL) + return(NULL); + + if (!p_start_rec) + p_rec = p_db->p_first_rec; + else + p_rec = p_start_rec->p_next_rec; + + while (p_rec) + { + p_attr = p_rec->p_first_attr; + while (p_attr) + { + if ((p_attr->attr_id == ATTR_ID_SERVICE_CLASS_ID_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + { + + printf("uuid len=%d ", p_uuid->len); + if (p_uuid->len == 2) + { + printf("uuid=0x%x \n", p_uuid->uu.uuid16); + } + else + { + printf("\n"); + } + + if (sdpu_compare_uuid_with_attr (p_uuid, p_sattr)) + return(p_rec); + } + } + break; + } + else if (p_attr->attr_id == ATTR_ID_SERVICE_ID) + { + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UUID_DESC_TYPE ) + { + if (sdpu_compare_uuid_with_attr (p_uuid, p_attr)) + return(p_rec); + } + } + + p_attr = p_attr->p_next_attr; + } + + p_rec = p_rec->p_next_rec; + } +#endif /* CLIENT_ENABLED == TRUE */ + /* If here, no matching UUID found */ + return(NULL); +} + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_fill_proto_elem +** +** Description This function retrieves the protocol element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +static BOOLEAN sdp_fill_proto_elem( tSDP_DISC_ATTR *p_attr, UINT16 layer_uuid, + tSDP_PROTOCOL_ELEM *p_elem) +{ + tSDP_DISC_ATTR *p_sattr; + + /* Walk through the protocol descriptor list */ + for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr) + { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + return(FALSE); + + /* Now, see if the entry contains the layer we are interested in */ + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + /* SDP_TRACE_DEBUG3 ("SDP - p_sattr 0x%x, layer_uuid:0x%x, u16:0x%x####", + p_sattr, layer_uuid, p_sattr->attr_value.v.u16); */ + + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) + && (p_sattr->attr_value.v.u16 == layer_uuid)) + { + /* Bingo. Now fill in the passed element */ + p_elem->protocol_uuid = layer_uuid; + p_elem->num_params = 0; + + /* Store the parameters, if any */ + for (p_sattr = p_sattr->p_next_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) != UINT_DESC_TYPE) + break; + + if (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) + p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u16; + else + p_elem->params[p_elem->num_params++] = p_sattr->attr_value.v.u8; + + if (p_elem->num_params >= SDP_MAX_PROTOCOL_PARAMS) + break; + } + return(TRUE); + } + } + } + + return(FALSE); +} +#endif /* CLIENT_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function SDP_FindProtocolListElemInRec +** +** Description This function looks at a specific discovery record for a protocol +** list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr; + + p_attr = p_rec->p_first_attr; + while (p_attr) + { + /* Find the protocol descriptor list */ + if ((p_attr->attr_id == ATTR_ID_PROTOCOL_DESC_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + return sdp_fill_proto_elem(p_attr, layer_uuid, p_elem); + } + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no match found */ + return(FALSE); +} + + +/******************************************************************************* +** +** Function SDP_FindAddProtoListsElemInRec +** +** Description This function looks at a specific discovery record for a protocol +** list element. +** +** Returns TRUE if found, FALSE if not +** If found, the passed protocol list element is filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindAddProtoListsElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr; + BOOLEAN ret = FALSE; + + p_attr = p_rec->p_first_attr; + while (p_attr) + { + /* Find the additional protocol descriptor list attribute */ + if ((p_attr->attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) + { + if ( (ret = sdp_fill_proto_elem(p_sattr, layer_uuid, p_elem)) == TRUE) + break; + } + } + return ret; + } + p_attr = p_attr->p_next_attr; + } +#endif + /* If here, no match found */ + return(FALSE); +} + + +/******************************************************************************* +** +** Function SDP_FindProfileVersionInRec +** +** Description This function looks at a specific discovery record for the +** Profile list descriptor, and pulls out the version number. +** The version number consists of an 8-bit major version and +** an 8-bit minor version. +** +** Returns TRUE if found, FALSE if not +** If found, the major and minor version numbers that were passed +** in are filled in. +** +*******************************************************************************/ +BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec, UINT16 profile_uuid, UINT16 *p_version) +{ +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISC_ATTR *p_attr, *p_sattr; + + p_attr = p_rec->p_first_attr; + while (p_attr) + { + /* Find the profile descriptor list */ + if ((p_attr->attr_id == ATTR_ID_BT_PROFILE_DESC_LIST) + && (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) + { + /* Walk through the protocol descriptor list */ + for (p_attr = p_attr->attr_value.v.p_sub_attr; p_attr; p_attr = p_attr->p_next_attr) + { + /* Safety check - each entry should itself be a sequence */ + if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE) + return(FALSE); + + /* Now, see if the entry contains the profile UUID we are interested in */ + for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr; p_sattr = p_sattr->p_next_attr) + { + if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UUID_DESC_TYPE) + && (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 2) /* <- This is bytes, not size code! */ + && (p_sattr->attr_value.v.u16 == profile_uuid)) + { + /* Now fill in the major and minor numbers */ + /* if the attribute matches the description for version (type UINT, size 2 bytes) */ + 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) == 2)) + { + /* The high order 8 bits is the major number, low order is the minor number (big endian) */ + *p_version = p_sattr->attr_value.v.u16; + + return(TRUE); + } + else + return(FALSE); /* The type and/or size was not valid for the profile list version */ + } + } + } + + return(FALSE); + } + p_attr = p_attr->p_next_attr; + } +#endif /* CLIENT_ENABLED == TRUE */ + + /* If here, no match found */ + return(FALSE); +} + +/******************************************************************************* +** Device Identification (DI) Client Functions +*******************************************************************************/ + +/******************************************************************************* +** +** Function SDP_DiDiscover +** +** Description This function queries a remote device for DI information. +** +** Returns SDP_SUCCESS if query started successfully, else error +** +*******************************************************************************/ +UINT16 SDP_DiDiscover( BD_ADDR remote_device, tSDP_DISCOVERY_DB *p_db, + UINT32 len, tSDP_DISC_CMPL_CB *p_cb ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 result = SDP_DI_DISC_FAILED; + UINT16 num_uuids = 1; + UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION; + + /* build uuid for db init */ + tSDP_UUID init_uuid; + init_uuid.len = 2; + init_uuid.uu.uuid16 = di_uuid; + + if ( SDP_InitDiscoveryDb(p_db, len, num_uuids, &init_uuid, 0, NULL) ) + if ( SDP_ServiceSearchRequest(remote_device, p_db, p_cb) ) + result = SDP_SUCCESS; + + return result; +#else + return SDP_DI_DISC_FAILED; +#endif +} + +/******************************************************************************* +** +** Function SDP_GetNumDiRecords +** +** Description Searches specified database for DI records +** +** Returns number of DI records found +** +*******************************************************************************/ +UINT8 SDP_GetNumDiRecords( tSDP_DISCOVERY_DB *p_db ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT8 num_records = 0; + tSDP_DISC_REC *p_curr_record = NULL; + + do + { + p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION, + p_curr_record ); + if ( p_curr_record ) + num_records++; + }while ( p_curr_record ); + + return num_records; +#else + return 0; +#endif +} + +/******************************************************************************* +** +** Function SDP_GetDiRecord +** +** Description This function retrieves a remote device's DI record from +** the specified database. +** +** Returns SDP_SUCCESS if record retrieved, else error +** +*******************************************************************************/ +UINT16 SDP_GetDiRecord( UINT8 get_record_index, tSDP_DI_GET_RECORD *p_device_info, + tSDP_DISCOVERY_DB *p_db ) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 result = SDP_NO_DI_RECORD_FOUND; + UINT8 curr_record_index = 1; + + tSDP_DISC_REC *p_curr_record = NULL; + + /* find the requested SDP record in the discovery database */ + do + { + p_curr_record = SDP_FindServiceInDb( p_db, UUID_SERVCLASS_PNP_INFORMATION, + p_curr_record ); + if ( p_curr_record ) + { + if ( curr_record_index++ == get_record_index ) + { + result = SDP_SUCCESS; + break; + } + } + }while ( p_curr_record ); + + if ( result == SDP_SUCCESS ) + { + /* copy the information from the SDP record to the DI record */ + tSDP_DISC_ATTR *p_curr_attr = NULL; + + /* ClientExecutableURL is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_CLIENT_EXE_URL ); + if ( p_curr_attr ) + BCM_STRNCPY_S( p_device_info->rec.client_executable_url, sizeof(p_device_info->rec.client_executable_url), + (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN ); + else + p_device_info->rec.client_executable_url[0] = '\0'; + + /* Service Description is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SERVICE_DESCRIPTION ); + if ( p_curr_attr ) + BCM_STRNCPY_S( p_device_info->rec.service_description, sizeof(p_device_info->rec.service_description), + (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN ); + else + p_device_info->rec.service_description[0] = '\0'; + + /* DocumentationURL is optional */ + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_DOCUMENTATION_URL ); + if ( p_curr_attr ) + BCM_STRNCPY_S( p_device_info->rec.documentation_url, sizeof(p_device_info->rec.documentation_url), + (char *)p_curr_attr->attr_value.v.array, SDP_MAX_ATTR_LEN ); + else + p_device_info->rec.documentation_url[0] = '\0'; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_SPECIFICATION_ID ); + if ( p_curr_attr ) + p_device_info->spec_id = p_curr_attr->attr_value.v.u16; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID ); + if ( p_curr_attr ) + p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_VENDOR_ID_SOURCE ); + if ( p_curr_attr ) + p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_ID ); + if ( p_curr_attr ) + p_device_info->rec.product = p_curr_attr->attr_value.v.u16; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRODUCT_VERSION ); + if ( p_curr_attr ) + p_device_info->rec.version = p_curr_attr->attr_value.v.u16; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + + p_curr_attr = SDP_FindAttributeInRec( p_curr_record, ATTR_ID_PRIMARY_RECORD ); + if ( p_curr_attr ) + p_device_info->rec.primary_record = (BOOLEAN)p_curr_attr->attr_value.v.u8; + else + result = SDP_ERR_ATTR_NOT_PRESENT; + } + + return result; +#else /* SDP_CLIENT_ENABLED is FALSE */ + return SDP_NO_DI_RECORD_FOUND; +#endif +} + +/******************************************************************************* +** Device Identification (DI) Server Functions +*******************************************************************************/ + +/******************************************************************************* +** +** Function SDP_SetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** +** +** Returns Returns SDP_SUCCESS if record added successfully, else error +** +*******************************************************************************/ +UINT16 SDP_SetLocalDiRecord( tSDP_DI_RECORD *p_device_info, UINT32 *p_handle ) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 result = SDP_SUCCESS; + UINT32 handle; + UINT16 di_uuid = UUID_SERVCLASS_PNP_INFORMATION; + UINT16 di_specid = BLUETOOTH_DI_SPECIFICATION; + UINT8 temp_u16[2]; + UINT8 *p_temp; + UINT8 u8; + + *p_handle = 0; + if ( p_device_info == NULL ) + return SDP_ILLEGAL_PARAMETER; + + /* if record is to be primary record, get handle to replace old primary */ + if ( p_device_info->primary_record == TRUE && sdp_cb.server_db.di_primary_handle ) + handle = sdp_cb.server_db.di_primary_handle; + else + { + if ( (handle = SDP_CreateRecord()) == 0 ) + return SDP_NO_RESOURCES; + } + + *p_handle = handle; + + /* build the SDP entry */ + /* Add the UUID to the Service Class ID List */ + if ((SDP_AddServiceClassIdList(handle, 1, &di_uuid)) == FALSE) + result = SDP_DI_REG_FAILED; + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, di_specid); + if ( !(SDP_AddAttribute(handle, ATTR_ID_SPECIFICATION_ID, + UINT_DESC_TYPE, sizeof(di_specid), + temp_u16)) ) + result = SDP_DI_REG_FAILED; + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) + { + if ( p_device_info->client_executable_url[0] != '\0' ) + { + if ( !((strlen(p_device_info->client_executable_url)+1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_CLIENT_EXE_URL, URL_DESC_TYPE, + (UINT32)(strlen(p_device_info->client_executable_url)+1), + (UINT8 *)p_device_info->client_executable_url)) ) + result = SDP_DI_REG_FAILED; + } + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) + { + if ( p_device_info->service_description[0] != '\0' ) + { + if ( !((strlen(p_device_info->service_description)+1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, + TEXT_STR_DESC_TYPE, + (UINT32)(strlen(p_device_info->service_description)+1), + (UINT8 *)p_device_info->service_description)) ) + result = SDP_DI_REG_FAILED; + } + } + + /* optional - if string is null, do not add attribute */ + if ( result == SDP_SUCCESS ) + { + if ( p_device_info->documentation_url[0] != '\0' ) + { + if ( !((strlen(p_device_info->documentation_url)+1 <= SDP_MAX_ATTR_LEN) && + SDP_AddAttribute(handle, ATTR_ID_DOCUMENTATION_URL, URL_DESC_TYPE, + (UINT32)(strlen(p_device_info->documentation_url)+1), + (UINT8 *)p_device_info->documentation_url)) ) + result = SDP_DI_REG_FAILED; + } + } + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor); + if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID, UINT_DESC_TYPE, + sizeof(p_device_info->vendor), temp_u16)) ) + result = SDP_DI_REG_FAILED; + } + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + p_temp = temp_u16; + UINT16_TO_BE_STREAM (p_temp, p_device_info->product); + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_ID, + UINT_DESC_TYPE, sizeof(p_device_info->product), temp_u16)) ) + result = SDP_DI_REG_FAILED; + } + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + p_temp = temp_u16; + UINT16_TO_BE_STREAM (p_temp, p_device_info->version); + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRODUCT_VERSION, UINT_DESC_TYPE, + sizeof(p_device_info->version), temp_u16)) ) + result = SDP_DI_REG_FAILED; + } + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + u8 = (UINT8)p_device_info->primary_record; + if ( !(SDP_AddAttribute(handle, ATTR_ID_PRIMARY_RECORD, + BOOLEAN_DESC_TYPE, 1, &u8)) ) + result = SDP_DI_REG_FAILED; + } + + /* mandatory */ + if ( result == SDP_SUCCESS) + { + p_temp = temp_u16; + UINT16_TO_BE_STREAM(p_temp, p_device_info->vendor_id_source); + if ( !(SDP_AddAttribute(handle, ATTR_ID_VENDOR_ID_SOURCE, UINT_DESC_TYPE, + sizeof(p_device_info->vendor_id_source), temp_u16)) ) + result = SDP_DI_REG_FAILED; + } + + if ( result != SDP_SUCCESS ) + SDP_DeleteRecord( handle ); + else if (p_device_info->primary_record == TRUE) + sdp_cb.server_db.di_primary_handle = handle; + + return result; +#else /* SDP_SERVER_ENABLED is FALSE */ + return SDP_DI_REG_FAILED; +#endif /* if SDP_SERVER_ENABLED */ +} + +/******************************************************************************* +** +** Function SDP_GetLocalDiRecord +** +** Description This function adds a DI record to the local SDP database. +** +** Fills in the device information of the record +** p_handle - if p_handle == 0, the primary record is returned +** +** Returns Returns SDP_SUCCESS if record exists, else error +** +*******************************************************************************/ +UINT16 SDP_GetLocalDiRecord(tSDP_DI_GET_RECORD *p_device_info, UINT32 *p_handle ) +{ + UINT16 result = SDP_NO_DI_RECORD_FOUND; + +#if SDP_SERVER_ENABLED == TRUE + tSDP_RECORD *p_rec; + tSDP_ATTRIBUTE *p_attr; + UINT8 *p_temp; + INT32 templen; + + if (*p_handle == 0) + *p_handle = sdp_cb.server_db.di_primary_handle; + + if ((p_rec = sdp_db_find_record(*p_handle)) != NULL) + { + memset(p_device_info, 0, sizeof(tSDP_DI_RECORD)); + + result = SDP_SUCCESS; + + /* Retrieve the Specification ID */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SPECIFICATION_ID, + ATTR_ID_SPECIFICATION_ID)) != NULL) + { + p_temp = p_attr->value_ptr; + BE_STREAM_TO_UINT16 (p_device_info->spec_id, p_temp); + } + + /* Retrieve the Vendor ID */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_VENDOR_ID, + ATTR_ID_VENDOR_ID)) != NULL) + { + p_temp = p_attr->value_ptr; + BE_STREAM_TO_UINT16 (p_device_info->rec.vendor, p_temp); + } + + /* Retrieve the Product ID */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRODUCT_ID, + ATTR_ID_PRODUCT_ID)) != NULL) + { + p_temp = p_attr->value_ptr; + BE_STREAM_TO_UINT16 (p_device_info->rec.product, p_temp); + } + + /* Retrieve the Version ID */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRODUCT_VERSION, + ATTR_ID_PRODUCT_VERSION)) != NULL) + { + p_temp = p_attr->value_ptr; + BE_STREAM_TO_UINT16 (p_device_info->rec.version, p_temp); + } + + /* Retrieve the Vendor ID Source ID */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_VENDOR_ID_SOURCE, + ATTR_ID_VENDOR_ID_SOURCE)) != NULL) + { + p_temp = p_attr->value_ptr; + BE_STREAM_TO_UINT16 (p_device_info->rec.vendor_id_source, p_temp); + } + + /* Retrieve the Primary Record */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_PRIMARY_RECORD, + ATTR_ID_PRIMARY_RECORD)) != NULL) + { + p_device_info->rec.primary_record = *p_attr->value_ptr; + } + + /* Retrieve the Client Executable URL */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_CLIENT_EXE_URL, + ATTR_ID_CLIENT_EXE_URL)) != NULL) + { + templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN); + p_temp = p_attr->value_ptr; + BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.client_executable_url, templen); + } + + /* Retrieve the Service Description */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_SERVICE_DESCRIPTION, + ATTR_ID_SERVICE_DESCRIPTION)) != NULL) + { + templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN); + p_temp = p_attr->value_ptr; + BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.service_description, templen); + } + + /* Retrieve the Documentation URL */ + if ((p_attr = sdp_db_find_attr_in_rec(p_rec, ATTR_ID_DOCUMENTATION_URL, + ATTR_ID_DOCUMENTATION_URL)) != NULL) + { + templen = (INT32)((p_attr->len < SDP_MAX_ATTR_LEN) ? p_attr->len : SDP_MAX_ATTR_LEN); + p_temp = p_attr->value_ptr; + BE_STREAM_TO_ARRAY (p_temp, p_device_info->rec.documentation_url, templen); + } + } + else + *p_handle = 0; +#endif + + return result; +} + + +/******************************************************************************* +** +** Function SDP_SetTraceLevel +** +** Description This function sets the trace level for SDP. If called with +** a value of 0xFF, it simply reads the current trace level. +** +** Returns the new (current) trace level +** +*******************************************************************************/ +UINT8 SDP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + sdp_cb.trace_level = new_level; + + return(sdp_cb.trace_level); +} + +#if SDP_FOR_JV_INCLUDED == TRUE +/******************************************************************************* +** +** Function SDP_ConnOpen +** +** Description This function creates a connection to the SDP server on the +** given device. +** +** Returns 0, if failed to initiate connection. Otherwise, the handle. +** +*******************************************************************************/ +UINT32 SDP_ConnOpen (UINT8 *p_bd_addr, tSDP_DISC_RES_CB *p_rcb, + tSDP_DISC_CMPL_CB *p_cb) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb; + UINT32 idx = 0; + + if (!p_cb || !p_rcb) + return(idx); + + /* Specific BD address */ + p_ccb = sdp_conn_originate (p_bd_addr); + + if (!p_ccb) + return(idx); + + p_ccb->disc_state = SDP_DISC_WAIT_CONN; + p_ccb->p_db = (tSDP_DISCOVERY_DB *)p_rcb; + p_ccb->p_cb = p_cb; + + p_ccb->is_attr_search = SDP_IS_PASS_THRU; + + idx = (UINT32)(p_ccb - sdp_cb.ccb); + return(UINT32)(idx + 1); +#else + return(0); +#endif +} + +/******************************************************************************* +** +** Function SDP_WriteData +** +** Description This function sends data to the connected SDP server. +** +** Returns TRUE if data is sent, FALSE if failed. +** +*******************************************************************************/ +BOOLEAN SDP_WriteData (UINT32 handle, BT_HDR *p_msg) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb = NULL; + + if (p_msg && (handle > 0) && (handle <= SDP_MAX_CONNECTIONS) ) + { + p_ccb = &sdp_cb.ccb[handle - 1]; + if ( (p_ccb->con_state == SDP_STATE_CONNECTED) && + (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) ) + { + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + L2CA_DataWrite (p_ccb->connection_id, p_msg); + return TRUE; + } + } +#endif + return FALSE; +} + +/******************************************************************************* +** +** Function SDP_ConnClose +** +** Description This function is called to close a SDP connection. +** +** Parameters: handle - Handle of the connection returned by SDP_ConnOpen +** +** Returns TRUE if connection is closed, FALSE if failed to find the handle. +** +*******************************************************************************/ +BOOLEAN SDP_ConnClose (UINT32 handle) +{ +#if SDP_CLIENT_ENABLED == TRUE + tCONN_CB *p_ccb = NULL; + + if (handle > 0 && handle <= SDP_MAX_CONNECTIONS) + { + p_ccb = &sdp_cb.ccb[handle - 1]; + sdp_disconnect (p_ccb, SDP_SUCCESS); + return TRUE; + } +#endif + return FALSE; +} +#endif diff --git a/stack/sdp/sdp_db.c b/stack/sdp/sdp_db.c new file mode 100644 index 0000000..45af000 --- /dev/null +++ b/stack/sdp/sdp_db.c @@ -0,0 +1,962 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains functions that handle the database + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" + +#include "gki.h" + +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "sdp_api.h" +#include "sdpint.h" +#include "wbt_api.h" + +#if SDP_SERVER_ENABLED == TRUE +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_his_uuid, + UINT16 his_len, int nest_level); + + +/******************************************************************************* +** +** Function sdp_db_service_search +** +** Description This function searches for a record that contains the +** specified UIDs. It is passed either NULL to start at the +** beginning, or the previous record found. +** +** Returns Pointer to the record, or NULL if not found. +** +*******************************************************************************/ +tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq) +{ + UINT16 xx, yy; + tSDP_ATTRIBUTE *p_attr; + tSDP_RECORD *p_end = &sdp_cb.server_db.record[sdp_cb.server_db.num_records]; + + /* If NULL, start at the beginning, else start at the first specified record */ + if (!p_rec) + p_rec = &sdp_cb.server_db.record[0]; + else + p_rec++; + + /* Look through the records. The spec says that a match occurs if */ + /* the record contains all the passed UUIDs in it. */ + for ( ; p_rec < p_end; p_rec++) + { + for (yy = 0; yy < p_seq->num_uids; yy++) + { + p_attr = &p_rec->attribute[0]; + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) + { + if (p_attr->type == UUID_DESC_TYPE) + { + if (sdpu_compare_uuid_arrays (p_attr->value_ptr, p_attr->len, + &p_seq->uuid_entry[yy].value[0], + p_seq->uuid_entry[yy].len)) + break; + } + else if (p_attr->type == DATA_ELE_SEQ_DESC_TYPE) + { + if (find_uuid_in_seq (p_attr->value_ptr, p_attr->len, + &p_seq->uuid_entry[yy].value[0], + p_seq->uuid_entry[yy].len, 0)) + break; + } + } + /* If any UUID was not found, on to the next record */ + if (xx == p_rec->num_attributes) + break; + } + + /* If every UUID was found in the record, return the record */ + if (yy == p_seq->num_uids) + return (p_rec); + } + + /* If here, no more records found */ + return (NULL); +} + +/******************************************************************************* +** +** Function find_uuid_in_seq +** +** Description This function searches a data element sequenct for a UUID. +** +** Returns TRUE if found, else FALSE +** +*******************************************************************************/ +static BOOLEAN find_uuid_in_seq (UINT8 *p , UINT32 seq_len, UINT8 *p_uuid, + UINT16 uuid_len, int nest_level) +{ + UINT8 *p_end = p + seq_len; + UINT8 type; + UINT32 len; + + /* A little safety check to avoid excessive recursion */ + if (nest_level > 3) + return (FALSE); + + while (p < p_end) + { + type = *p++; + p = sdpu_get_len_from_type (p, type, &len); + type = type >> 3; + if (type == UUID_DESC_TYPE) + { + if (sdpu_compare_uuid_arrays (p, len, p_uuid, uuid_len)) + return (TRUE); + } + else if (type == DATA_ELE_SEQ_DESC_TYPE) + { + if (find_uuid_in_seq (p, len, p_uuid, uuid_len, nest_level + 1)) + return (TRUE); + } + p = p + len; + } + + /* If here, failed to match */ + return (FALSE); +} + +/******************************************************************************* +** +** Function sdp_db_find_record +** +** Description This function searches for a record with a specific handle +** It is passed the handle of the record. +** +** Returns Pointer to the record, or NULL if not found. +** +*******************************************************************************/ +tSDP_RECORD *sdp_db_find_record (UINT32 handle) +{ + tSDP_RECORD *p_rec; + tSDP_RECORD *p_end = &sdp_cb.server_db.record[sdp_cb.server_db.num_records]; + + /* Look through the records for the caller's handle */ + for (p_rec = &sdp_cb.server_db.record[0]; p_rec < p_end; p_rec++) + { + if (p_rec->record_handle == handle) + return (p_rec); + } + + /* Record with that handle not found. */ + return (NULL); +} + +/******************************************************************************* +** +** Function sdp_db_find_attr_in_rec +** +** Description This function searches a record for specific attributes. +** It is passed a pointer to the record. If the record contains +** the specified attribute, (the caller may specify be a range +** of attributes), the attribute is returned. +** +** Returns Pointer to the attribute, or NULL if not found. +** +*******************************************************************************/ +tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, + UINT16 end_attr) +{ + tSDP_ATTRIBUTE *p_at; + UINT16 xx; + + /* Note that the attributes in a record are assumed to be in sorted order */ + for (xx = 0, p_at = &p_rec->attribute[0]; xx < p_rec->num_attributes; + xx++, p_at++) + { + if ((p_at->id >= start_attr) && (p_at->id <= end_attr)) + return (p_at); + } + + /* No matching attribute found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdp_compose_proto_list +** +** Description This function is called to compose a data sequence from +** protocol element list struct pointer +** +** Returns the length of the data sequence +** +*******************************************************************************/ +static int sdp_compose_proto_list( UINT8 *p, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list) +{ + UINT16 xx, yy, len; + BOOLEAN is_rfcomm_scn; + UINT8 *p_head = p; + UINT8 *p_len; + + /* First, build the protocol list. This consists of a set of data element + ** sequences, one for each layer. Each layer sequence consists of layer's + ** UUID and optional parameters + */ + for (xx = 0; xx < num_elem; xx++, p_elem_list++) + { + len = 3 + (p_elem_list->num_params * 3); + UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + + p_len = p; + *p++ = (UINT8) len; + + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, p_elem_list->protocol_uuid); + + if (p_elem_list->protocol_uuid == UUID_PROTOCOL_RFCOMM) + is_rfcomm_scn = TRUE; + else + is_rfcomm_scn = FALSE; + + for (yy = 0; yy < p_elem_list->num_params; yy++) + { + if (is_rfcomm_scn) + { + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + UINT8_TO_BE_STREAM (p, p_elem_list->params[yy]); + + *p_len -= 1; + } + else + { + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, p_elem_list->params[yy]); + } + } + } + return (p - p_head); +} + +#endif /* SDP_SERVER_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function SDP_CreateRecord +** +** Description This function is called to create a record in the database. +** This would be through the SDP database maintenance API. The +** record is created empty, teh application should then call +** "add_attribute" to add the record's attributes. +** +** Returns Record handle if OK, else 0. +** +*******************************************************************************/ +UINT32 SDP_CreateRecord (void) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT32 handle; + UINT8 buf[4]; + tSDP_DB *p_db = &sdp_cb.server_db; + + /* First, check if there is a free record */ + if (p_db->num_records < SDP_MAX_RECORDS) + { + memset (&p_db->record[p_db->num_records], 0, + sizeof (tSDP_RECORD)); + + /* We will use a handle of the first unreserved handle plus last record + ** number + 1 */ + if (p_db->num_records) + handle = p_db->record[p_db->num_records - 1].record_handle + 1; + else + handle = 0x10000; + + p_db->record[p_db->num_records].record_handle = handle; + + p_db->num_records++; + + /* Add the first attribute (the handle) automatically */ + UINT32_TO_BE_FIELD (buf, handle); + SDP_AddAttribute (handle, ATTR_ID_SERVICE_RECORD_HDL, UINT_DESC_TYPE, + 4, buf); + + return (p_db->record[p_db->num_records - 1].record_handle); + } +#endif + return (0); +} + + +/******************************************************************************* +** +** Function SDP_DeleteRecord +** +** Description This function is called to add a record (or all records) +** from the database. This would be through the SDP database +** maintenance API. +** +** If a record handle of 0 is passed, all records are deleted. +** +** Returns TRUE if succeeded, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_DeleteRecord (UINT32 handle) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx, yy, zz; + tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0]; + + if (handle == 0 || sdp_cb.server_db.num_records == 0) + { + /* Delete all records in the database */ + sdp_cb.server_db.num_records = 0; + + /* require new DI record to be created in SDP_SetLocalDiRecord */ + sdp_cb.server_db.di_primary_handle = 0; + sdp_cb.server_db.brcm_di_registered = 0; + + return (TRUE); + } + else + { + /* Find the record in the database */ + for (xx = 0; xx < sdp_cb.server_db.num_records; xx++, p_rec++) + { + if (p_rec->record_handle == handle) + { + /* Found it. Shift everything up one */ + for (yy = xx; yy < sdp_cb.server_db.num_records; yy++, p_rec++) + { + *p_rec = *(p_rec + 1); + + /* Adjust the attribute value pointer for each attribute */ + for (zz = 0; zz < p_rec->num_attributes; zz++) + p_rec->attribute[zz].value_ptr -= sizeof(tSDP_RECORD); + } + + sdp_cb.server_db.num_records--; + + /* if we're deleting the primary DI record, clear the */ + /* value in the control block */ + if( sdp_cb.server_db.di_primary_handle == handle ) + { + sdp_cb.server_db.di_primary_handle = 0; + sdp_cb.server_db.brcm_di_registered = 0; + } + + return (TRUE); + } + } + } +#endif + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_AddAttribute +** +** Description This function is called to add an attribute to a record. +** This would be through the SDP database maintenance API. +** If the attribute already exists in the record, it is replaced +** with the new value. +** +** NOTE Attribute values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddAttribute (UINT32 handle, UINT16 attr_id, UINT8 attr_type, + UINT32 attr_len, UINT8 *p_val) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx, yy, zz; + tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0]; + +#if (BT_TRACE_VERBOSE == TRUE) + if (sdp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG) + { + if ((attr_type == UINT_DESC_TYPE) || + (attr_type == TWO_COMP_INT_DESC_TYPE) || + (attr_type == UUID_DESC_TYPE) || + (attr_type == DATA_ELE_SEQ_DESC_TYPE) || + (attr_type == DATA_ELE_ALT_DESC_TYPE)) + { + UINT8 num_array[400]; + UINT32 i; + UINT32 len = (attr_len > 200) ? 200 : attr_len; + + num_array[0] ='\0'; + for (i = 0; i < len; i++) + { + sprintf((char *)&num_array[i*2],"%02X",(UINT8)(p_val[i])); + } + SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s", + handle,attr_id,attr_type,attr_len,p_val,num_array); + } + else if (attr_type == BOOLEAN_DESC_TYPE) + { + SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%d", + handle,attr_id,attr_type,attr_len,p_val,*p_val); + } + else + { + SDP_TRACE_DEBUG6("SDP_AddAttribute: handle:%X, id:%04X, type:%d, len:%d, p_val:%p, *p_val:%s", + handle,attr_id,attr_type,attr_len,p_val,p_val); + } + } +#endif + + /* Find the record in the database */ + for (zz = 0; zz < sdp_cb.server_db.num_records; zz++, p_rec++) + { + if (p_rec->record_handle == handle) + { + tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0]; + + /* Found the record. Now, see if the attribute already exists */ + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) + { + /* The attribute exists. replace it */ + if (p_attr->id == attr_id) + { + SDP_DeleteAttribute (handle, attr_id); + break; + } + if (p_attr->id > attr_id) + break; + } + + if (p_rec->num_attributes == SDP_MAX_REC_ATTR) + return (FALSE); + + /* If not found, see if we can allocate a new entry */ + if (xx == p_rec->num_attributes) + p_attr = &p_rec->attribute[p_rec->num_attributes]; + else + { + /* Since the attributes are kept in sorted order, insert ours here */ + for (yy = p_rec->num_attributes; yy > xx; yy--) + p_rec->attribute[yy] = p_rec->attribute[yy - 1]; + } + + p_attr->id = attr_id; + p_attr->type = attr_type; + p_attr->len = attr_len; + + if (p_rec->free_pad_ptr + attr_len >= SDP_MAX_PAD_LEN) + { + /* do truncate only for text string type descriptor */ + if (attr_type == TEXT_STR_DESC_TYPE) + { + SDP_TRACE_WARNING2("SDP_AddAttribute: attr_len:%d too long. truncate to (%d)", + attr_len, SDP_MAX_PAD_LEN - p_rec->free_pad_ptr ); + + attr_len = SDP_MAX_PAD_LEN - p_rec->free_pad_ptr; + p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr] = '\0'; + p_val[SDP_MAX_PAD_LEN - p_rec->free_pad_ptr+1] = '\0'; + } + else + attr_len = 0; + } + + if ((attr_len > 0) && (p_val != 0)) + { + p_attr->len = attr_len; + memcpy (&p_rec->attr_pad[p_rec->free_pad_ptr], p_val, (size_t)attr_len); + p_attr->value_ptr = &p_rec->attr_pad[p_rec->free_pad_ptr]; + p_rec->free_pad_ptr += attr_len; + } + else if ((attr_len == 0 && p_attr->len != 0) || /* if truncate to 0 length, simply don't add */ + p_val == 0) + { + SDP_TRACE_ERROR2("SDP_AddAttribute fail, length exceed maximum: ID %d: attr_len:%d ", + attr_id, attr_len ); + p_attr->id = p_attr->type = p_attr->len = 0; + return (FALSE); + } + p_rec->num_attributes++; + + /*** Mark DI record as used by Broadcom ***/ + if (handle == sdp_cb.server_db.di_primary_handle && + attr_id == ATTR_ID_EXT_BRCM_VERSION) + sdp_cb.server_db.brcm_di_registered = TRUE; + + return (TRUE); + } + } +#endif + return (FALSE); +} + + +/******************************************************************************* +** +** Function SDP_AddSequence +** +** Description This function is called to add a sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** NOTE Element values must be passed as a Big Endian stream. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddSequence (UINT32 handle, UINT16 attr_id, UINT16 num_elem, + UINT8 type[], UINT8 len[], UINT8 *p_val[]) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 buff[SDP_MAX_ATTR_LEN * 2]; + UINT8 *p; + UINT8 *p_head; + + p = buff; + /* First, build the sequence */ + for (xx = 0; xx < num_elem; xx++) + { + p_head = p; + switch (len[xx]) + { + case 1: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_ONE_BYTE); + break; + case 2: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_TWO_BYTES); + break; + case 4: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_FOUR_BYTES); + break; + case 8: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_EIGHT_BYTES); + break; + case 16: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_SIXTEEN_BYTES); + break; + default: + UINT8_TO_BE_STREAM (p, (type[xx] << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p, len[xx]); + break; + } + + ARRAY_TO_BE_STREAM (p, p_val[xx], len[xx]); + + if (p - buff > SDP_MAX_ATTR_LEN) + { + /* go back to before we add this element */ + p = p_head; + if(p_head == buff) + { + /* the first element exceed the max length */ + SDP_TRACE_ERROR0 ("SDP_AddSequence - too long(attribute is not added)!!"); + return FALSE; + } + else + SDP_TRACE_ERROR2 ("SDP_AddSequence - too long, add %d elements of %d", xx, num_elem); + break; + } + } + + return (SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddUuidSequence +** +** Description This function is called to add a UUID sequence to a record. +** This would be through the SDP database maintenance API. +** If the sequence already exists in the record, it is replaced +** with the new sequence. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddUuidSequence (UINT32 handle, UINT16 attr_id, UINT16 num_uuids, + UINT16 *p_uuids) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 buff[SDP_MAX_ATTR_LEN * 2]; + UINT8 *p = buff; + INT32 max_len = SDP_MAX_ATTR_LEN -3; + + /* First, build the sequence */ + for (xx = 0; xx < num_uuids ; xx++, p_uuids++) + { + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, *p_uuids); + + if((p - buff) > max_len) + { + SDP_TRACE_WARNING2 ("SDP_AddUuidSequence - too long, add %d uuids of %d", xx, num_uuids); + break; + } + } + + return (SDP_AddAttribute (handle, attr_id, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + +/******************************************************************************* +** +** Function SDP_AddProtocolList +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddProtocolList (UINT32 handle, UINT16 num_elem, + tSDP_PROTOCOL_ELEM *p_elem_list) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 buff[SDP_MAX_ATTR_LEN * 2]; + int offset; + + offset = sdp_compose_proto_list(buff, num_elem, p_elem_list); + + return (SDP_AddAttribute (handle, ATTR_ID_PROTOCOL_DESC_LIST, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) offset, buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddAdditionProtoLists +** +** Description This function is called to add a protocol descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the protocol list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddAdditionProtoLists (UINT32 handle, UINT16 num_elem, + tSDP_PROTO_LIST_ELEM *p_proto_list) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 buff[SDP_MAX_ATTR_LEN * 2]; + UINT8 *p = buff; + UINT8 *p_len; + int offset; + + /* for each ProtocolDescriptorList */ + for (xx = 0; xx < num_elem; xx++, p_proto_list++) + { + UINT8_TO_BE_STREAM (p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_len = p++; + + offset = sdp_compose_proto_list(p, p_proto_list->num_elems, + p_proto_list->list_elem); + p += offset; + + *p_len = (UINT8)(p - p_len - 1); + } + + return (SDP_AddAttribute (handle, ATTR_ID_ADDITION_PROTO_DESC_LISTS, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + +/******************************************************************************* +** +** Function SDP_AddProfileDescriptorList +** +** Description This function is called to add a profile descriptor list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddProfileDescriptorList (UINT32 handle, UINT16 profile_uuid, + UINT16 version) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 buff[SDP_MAX_ATTR_LEN]; + UINT8 *p = &buff[2]; + + /* First, build the profile descriptor list. This consists of a data element sequence. */ + /* The sequence consists of profile's UUID and version number */ + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, profile_uuid); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, version); + + /* Add in type and length fields */ + buff[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + buff[1] = (UINT8) (p - &buff[2]); + + return (SDP_AddAttribute (handle, ATTR_ID_BT_PROFILE_DESC_LIST, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddLanguageBaseAttrIDList +** +** Description This function is called to add a language base attr list to +** a record. This would be through the SDP database maintenance API. +** If the version already exists in the record, it is replaced +** with the new one. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddLanguageBaseAttrIDList (UINT32 handle, UINT16 lang, + UINT16 char_enc, UINT16 base_id) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT8 buff[SDP_MAX_ATTR_LEN]; + UINT8 *p = buff; + + /* First, build the language base descriptor list. This consists of a data */ + /* element sequence. The sequence consists of 9 bytes (3 UINt16 fields) */ + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, lang); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, char_enc); + + UINT8_TO_BE_STREAM (p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, base_id); + + return (SDP_AddAttribute (handle, ATTR_ID_LANGUAGE_BASE_ATTR_ID_LIST, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_AddServiceClassIdList +** +** Description This function is called to add a service list to a record. +** This would be through the SDP database maintenance API. +** If the service list already exists in the record, it is replaced +** with the new list. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN SDP_AddServiceClassIdList (UINT32 handle, UINT16 num_services, + UINT16 *p_service_uuids) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx; + UINT8 buff[SDP_MAX_ATTR_LEN * 2]; + UINT8 *p = buff; + + for (xx = 0; xx < num_services; xx++, p_service_uuids++) + { + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p, *p_service_uuids); + } + + return (SDP_AddAttribute (handle, ATTR_ID_SERVICE_CLASS_ID_LIST, + DATA_ELE_SEQ_DESC_TYPE, (UINT32) (p - buff), buff)); +#else /* SDP_SERVER_ENABLED == FALSE */ + return (FALSE); +#endif +} + + +/******************************************************************************* +** +** Function SDP_DeleteAttribute +** +** Description This function is called to delete an attribute from a record. +** This would be through the SDP database maintenance API. +** +** Returns TRUE if deleted OK, else FALSE if not found +** +*******************************************************************************/ +BOOLEAN SDP_DeleteAttribute (UINT32 handle, UINT16 attr_id) +{ +#if SDP_SERVER_ENABLED == TRUE + UINT16 xx, yy; + tSDP_RECORD *p_rec = &sdp_cb.server_db.record[0]; + UINT8 *pad_ptr; + UINT32 len; /* Number of bytes in the entry */ + + /* Find the record in the database */ + for (xx = 0; xx < sdp_cb.server_db.num_records; xx++, p_rec++) + { + if (p_rec->record_handle == handle) + { + tSDP_ATTRIBUTE *p_attr = &p_rec->attribute[0]; + + SDP_TRACE_API2("Deleting attr_id 0x%04x for handle 0x%x", attr_id, handle); + /* Found it. Now, find the attribute */ + for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) + { + if (p_attr->id == attr_id) + { + pad_ptr = p_attr->value_ptr; + len = p_attr->len; + + if (len) + { + for (yy = 0; yy < p_rec->num_attributes; yy++) + { + if( p_rec->attribute[yy].value_ptr > pad_ptr ) + p_rec->attribute[yy].value_ptr -= len; + } + } + + /* Found it. Shift everything up one */ + p_rec->num_attributes--; + + for (yy = xx; yy < p_rec->num_attributes; yy++, p_attr++) + { + *p_attr = *(p_attr + 1); + } + + /* adjust attribute values if needed */ + if (len) + { + xx = (p_rec->free_pad_ptr - ((pad_ptr+len) - + &p_rec->attr_pad[0])); + for( yy=0; yyfree_pad_ptr -= len; + } + return (TRUE); + } + } + } + } +#endif + /* If here, not found */ + return (FALSE); +} + +/******************************************************************************* +** +** Function SDP_ReadRecord +** +** Description This function is called to get the raw data of the record +** with the given handle from the database. +** +** Returns -1, if the record is not found. +** Otherwise, the offset (0 or 1) to start of data in p_data. +** +** The size of data copied into p_data is in *p_data_len. +** +*******************************************************************************/ +#if (SDP_RAW_DATA_INCLUDED == TRUE) +INT32 SDP_ReadRecord(UINT32 handle, UINT8 *p_data, INT32 *p_data_len) +{ + INT32 len = 0; /* Number of bytes in the entry */ + INT32 offset = -1; /* default to not found */ +#if SDP_SERVER_ENABLED == TRUE + tSDP_RECORD *p_rec; + UINT16 start = 0; + UINT16 end = 0xffff; + tSDP_ATTRIBUTE *p_attr; + UINT16 rem_len; + UINT8 *p_rsp; + + /* Find the record in the database */ + p_rec = sdp_db_find_record(handle); + if(p_rec && p_data && p_data_len) + { + p_rsp = &p_data[3]; + while ( (p_attr = sdp_db_find_attr_in_rec (p_rec, start, end)) != NULL) + { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = *p_data_len - (UINT16) (p_rsp - p_data); + + if (p_attr->len > (UINT32)(rem_len - 6)) + break; + + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + + /* next attr id */ + start = p_attr->id + 1; + } + len = (INT32) (p_rsp - p_data); + + /* Put in the sequence header (2 or 3 bytes) */ + if (len > 255) + { + offset = 0; + p_data[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_data[1] = (UINT8) ((len - 3) >> 8); + p_data[2] = (UINT8) (len - 3); + } + else + { + offset = 1; + + p_data[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_data[2] = (UINT8) (len - 3); + + len--; + } + *p_data_len = len; + } +#endif + /* If here, not found */ + return (offset); +} +#endif + + + diff --git a/stack/sdp/sdp_discovery.c b/stack/sdp/sdp_discovery.c new file mode 100644 index 0000000..1ad1336 --- /dev/null +++ b/stack/sdp/sdp_discovery.c @@ -0,0 +1,1127 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains SDP discovery functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" +#include "gki.h" +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" +#include "sdp_api.h" +#include "sdpint.h" +#include "btu.h" +#include "btm_api.h" + + +#ifndef SDP_DEBUG_RAW +#define SDP_DEBUG_RAW FALSE +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +#if SDP_CLIENT_ENABLED == TRUE +static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len); +static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len); +static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len); +static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end); +static tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda); +static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec, + UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level); + +/* Safety check in case we go crazy */ +#define MAX_NEST_LEVELS 5 + + +/******************************************************************************* +** +** Function sdpu_build_uuid_seq +** +** Description This function builds a UUID sequence from the list of +** passed UUIDs. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +static UINT8 *sdpu_build_uuid_seq (UINT8 *p_out, UINT16 num_uuids, tSDP_UUID *p_uuid_list) +{ + UINT16 xx; + UINT8 *p_len; + + /* First thing is the data element header */ + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + + /* Remember where the length goes. Leave space for it. */ + p_len = p_out; + p_out += 1; + + /* Now, loop through and put in all the UUID(s) */ + for (xx = 0; xx < num_uuids; xx++, p_uuid_list++) + { + if (p_uuid_list->len == 2) + { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid16); + } + else if (p_uuid_list->len == 4) + { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT32_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid32); + } + else + { + UINT8_TO_BE_STREAM (p_out, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM (p_out, p_uuid_list->uu.uuid128, p_uuid_list->len); + } + } + + /* Now, put in the length */ + xx = (UINT16)(p_out - p_len - 1); + UINT8_TO_BE_STREAM (p_len, xx); + + return (p_out); +} + +/******************************************************************************* +** +** Function sdp_snd_service_search_req +** +** Description Send a service search request to the SDP server. +** +** Returns void +** +*******************************************************************************/ +static void sdp_snd_service_search_req(tCONN_CB *p_ccb, UINT8 cont_len, UINT8 * p_cont) +{ + UINT8 *p, *p_start, *p_param_len; + BT_HDR *p_cmd; + UINT16 param_len; + + /* Get a buffer to send the packet to L2CAP */ + if ((p_cmd = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID)) == NULL) + { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_cmd->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_cmd + 1) + L2CAP_MIN_OFFSET; + + /* Build a service search request packet */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + /* Build the UID sequence. */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]); +#else + p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters); +#endif + + /* Set max service record count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_recs_per_search); + + /* Set continuation state */ + UINT8_TO_BE_STREAM (p, cont_len); + + /* if this is not the first request */ + if(cont_len && p_cont) + { + memcpy(p, p_cont, cont_len); + p += cont_len; + } + + /* Go back and put the parameter length into the buffer */ + param_len = (UINT16)(p - p_param_len - 2); + UINT16_TO_BE_STREAM (p_param_len, param_len); + + p_ccb->disc_state = SDP_DISC_WAIT_HANDLES; + + /* Set the length of the SDP data in the buffer */ + p_cmd->len = (UINT16)(p - p_start); + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING2("sdp_snd_service_search_req cont_len :%d disc_state:%d",cont_len, p_ccb->disc_state); +#endif + + + L2CA_DataWrite (p_ccb->connection_id, p_cmd); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + +} + +/******************************************************************************* +** +** Function sdp_disc_connected +** +** Description This function is called when an SDP discovery attempt is +** connected. +** +** Returns void +** +*******************************************************************************/ +void sdp_disc_connected (tCONN_CB *p_ccb) +{ + +#if SDP_FOR_JV_INCLUDED == TRUE + if (SDP_IS_PASS_THRU == p_ccb->is_attr_search) + { + tSDP_DISC_RES_CB *p_rcb = (tSDP_DISC_RES_CB *) p_ccb->p_db; + tSDP_DR_OPEN evt_data; + /* report connected */ + p_ccb->disc_state = SDP_DISC_WAIT_PASS_THRU; + if (p_rcb) + { + memcpy(evt_data.peer_addr, p_ccb->device_address, BD_ADDR_LEN); + evt_data.peer_mtu = p_ccb->rem_mtu_size; + (*p_rcb)(SDP_EVT_OPEN, (void *)&evt_data); + } + } + else +#endif + if (p_ccb->is_attr_search) + { + p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR; + + process_service_search_attr_rsp (p_ccb, NULL, 0); + } + else + { + /* First step is to get a list of the handles from the server. */ + /* We are not searching for a specific attribute, so we will */ + /* first search for the service, then get all attributes of it */ + + p_ccb->num_handles = 0; + sdp_snd_service_search_req(p_ccb, 0, NULL); + } + +} + +/******************************************************************************* +** +** Function sdp_disc_server_rsp +** +** Description This function is called when there is a response from +** the server. +** +** Returns void +** +*******************************************************************************/ +void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg) +{ + UINT8 *p, rsp_pdu; + BOOLEAN invalid_pdu = TRUE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("sdp_disc_server_rsp disc_state:%d", p_ccb->disc_state); +#endif + + /* stop inactivity timer when we receive a response */ + btu_stop_timer (&p_ccb->timer_entry); + +#if SDP_FOR_JV_INCLUDED == TRUE + if(SDP_IS_PASS_THRU == p_ccb->is_attr_search) + { + tSDP_DISC_RES_CB *p_rcb = (tSDP_DISC_RES_CB *) p_ccb->p_db; + tSDP_DR_DATA data; + if (p_rcb) + { + data.p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + data.data_len = p_msg->len; + (*p_rcb)(SDP_EVT_DATA_IND, (void *)&data); + } + return; + } +#endif + + /* Got a reply!! Check what we got back */ + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + BE_STREAM_TO_UINT8 (rsp_pdu, p); + + p_msg->len--; + + switch (rsp_pdu) + { + case SDP_PDU_SERVICE_SEARCH_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_HANDLES) + { + process_service_search_rsp (p_ccb, p, p_msg->len); + invalid_pdu = FALSE; + } + break; + + case SDP_PDU_SERVICE_ATTR_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_ATTR) + { + process_service_attr_rsp (p_ccb, p, p_msg->len); + invalid_pdu = FALSE; + } + break; + + case SDP_PDU_SERVICE_SEARCH_ATTR_RSP: + if (p_ccb->disc_state == SDP_DISC_WAIT_SEARCH_ATTR) + { + process_service_search_attr_rsp (p_ccb, p, p_msg->len); + invalid_pdu = FALSE; + } + break; + } + + if (invalid_pdu) + { + SDP_TRACE_WARNING2 ("SDP - Unexp. PDU: %d in state: %d", rsp_pdu, p_ccb->disc_state); + sdp_disconnect (p_ccb, SDP_GENERIC_ERROR); + } +} + +/****************************************************************************** +** +** Function process_service_search_rsp +** +** Description This function is called when there is a search response from +** the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len) +{ + UINT16 xx; + UINT16 total, cur_handles, orig; + UINT8 cont_len; + + /* Skip transaction, and param len */ + p_reply += 4; + BE_STREAM_TO_UINT16 (total, p_reply); + BE_STREAM_TO_UINT16 (cur_handles, p_reply); + + orig = p_ccb->num_handles; + p_ccb->num_handles += cur_handles; + if (p_ccb->num_handles == 0) + { + SDP_TRACE_WARNING0 ("SDP - Rcvd ServiceSearchRsp, no matches"); + sdp_disconnect (p_ccb, SDP_NO_RECS_MATCH); + return; + } + + /* Save the handles that match. We will can only process a certain number. */ + if (total > sdp_cb.max_recs_per_search) + total = sdp_cb.max_recs_per_search; + if (p_ccb->num_handles > sdp_cb.max_recs_per_search) + p_ccb->num_handles = sdp_cb.max_recs_per_search; + + for (xx = orig; xx < p_ccb->num_handles; xx++) + BE_STREAM_TO_UINT32 (p_ccb->handles[xx], p_reply); + + BE_STREAM_TO_UINT8 (cont_len, p_reply); + if(cont_len != 0) + { + if(cont_len > SDP_MAX_CONTINUATION_LEN) + { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + /* stay in the same state */ + sdp_snd_service_search_req(p_ccb, cont_len, p_reply); + } + else + { + /* change state */ + p_ccb->disc_state = SDP_DISC_WAIT_ATTR; + + /* Kick off the first attribute request */ + process_service_attr_rsp (p_ccb, NULL, 0); + } +} + +/******************************************************************************* +** +** Function sdp_copy_raw_data +** +** Description copy the raw data +** +** +** Returns void +** +*******************************************************************************/ +#if (SDP_RAW_DATA_INCLUDED == TRUE) +static void sdp_copy_raw_data (tCONN_CB *p_ccb, UINT16 len, BOOLEAN offset) +{ + unsigned int cpy_len; + UINT32 list_len; + UINT8 *p; + UINT8 * p_temp; + UINT8 type; + UINT32 delta_len = 0; + +#if (SDP_DEBUG_RAW == TRUE) + UINT8 num_array[SDP_MAX_LIST_BYTE_COUNT]; + UINT32 i; + + for (i = 0; i < p_ccb->list_len; i++) + { + sprintf((char *)&num_array[i*2],"%02X",(UINT8)(p_ccb->rsp_list[i])); + } + SDP_TRACE_WARNING1("result :%s",num_array); +#endif + + if(p_ccb->p_db->raw_data) + { + cpy_len = p_ccb->p_db->raw_size - p_ccb->p_db->raw_used; + list_len = p_ccb->list_len; + p_temp = p = &p_ccb->rsp_list[0]; + + if(offset) + { + type = *p++; + p = sdpu_get_len_from_type (p, type, &list_len); + } + if(list_len && list_len < cpy_len ) + { + cpy_len = list_len; + } +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING4("list_len :%d cpy_len:%d raw_size:%d raw_used:%d", + list_len, cpy_len, p_ccb->p_db->raw_size, p_ccb->p_db->raw_used); +#endif + memcpy (&p_ccb->p_db->raw_data[p_ccb->p_db->raw_used], p, cpy_len); + p_ccb->p_db->raw_used += cpy_len; + } +} +#endif + +/******************************************************************************* +** +** Function process_service_attr_rsp +** +** Description This function is called when there is a attribute response from +** the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len) +{ + UINT8 *p_start, *p_param_len; + UINT16 param_len, list_byte_count; + BOOLEAN cont_request_needed = FALSE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING2("process_service_attr_rsp len:%d raw inc:%d", + len, SDP_RAW_DATA_INCLUDED); +#endif + /* If p_reply is NULL, we were called after the records handles were read */ + if (p_reply) + { +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING4("ID & len: 0x%02x-%02x-%02x-%02x", + p_reply[0], p_reply[1], p_reply[2], p_reply[3]); +#endif + /* Skip transaction ID and length */ + p_reply += 4; + + BE_STREAM_TO_UINT16 (list_byte_count, p_reply); +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("list_byte_count:%d", list_byte_count); +#endif + + /* Copy the response to the scratchpad. First, a safety check on the length */ + if ((p_ccb->list_len + list_byte_count) > SDP_MAX_LIST_BYTE_COUNT) + { + sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE); + return; + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING2("list_len: %d, list_byte_count: %d", + p_ccb->list_len, list_byte_count); +#endif + if (p_ccb->rsp_list == NULL) + { + p_ccb->rsp_list = (UINT8 *)GKI_getbuf (SDP_MAX_LIST_BYTE_COUNT); + if (p_ccb->rsp_list == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no gki buf to save rsp"); + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + } + memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, list_byte_count); + p_ccb->list_len += list_byte_count; + p_reply += list_byte_count; +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("list_len: %d(attr_rsp)", p_ccb->list_len); + + /* Check if we need to request a continuation */ + SDP_TRACE_WARNING2("*p_reply:%d(%d)", *p_reply, SDP_MAX_CONTINUATION_LEN); +#endif + if (*p_reply) + { + if (*p_reply > SDP_MAX_CONTINUATION_LEN) + { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + cont_request_needed = TRUE; + } + else + { + +#if (SDP_RAW_DATA_INCLUDED == TRUE) + SDP_TRACE_WARNING0("process_service_attr_rsp"); + sdp_copy_raw_data (p_ccb, len, FALSE); +#endif + + /* Save the response in the database. Stop on any error */ + if (!save_attr_seq (p_ccb, &p_ccb->rsp_list[0], &p_ccb->rsp_list[p_ccb->list_len])) + { + sdp_disconnect (p_ccb, SDP_DB_FULL); + return; + } + p_ccb->list_len = 0; + p_ccb->cur_handle++; + } + } + + /* Now, ask for the next handle. Re-use the buffer we just got. */ + if (p_ccb->cur_handle < p_ccb->num_handles) + { + BT_HDR *p_msg = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID); + UINT8 *p; + + if (!p_msg) + { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_msg->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + /* Get all the attributes from the server */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_ATTR_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + UINT32_TO_BE_STREAM (p, p_ccb->handles[p_ccb->cur_handle]); + + /* Max attribute byte count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size); + + /* If no attribute filters, build a wildcard attribute sequence */ + if (p_ccb->p_db->num_attr_filters) + p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters); + else + p = sdpu_build_attrib_seq (p, NULL, 0); + + /* Was this a continuation request ? */ + if (cont_request_needed) + { + memcpy (p, p_reply, *p_reply + 1); + p += *p_reply + 1; + } + else + UINT8_TO_BE_STREAM (p, 0); + + /* Go back and put the parameter length into the buffer */ + param_len = (UINT16)(p - p_param_len - 2); + UINT16_TO_BE_STREAM (p_param_len, param_len); + + /* Set the length of the SDP data in the buffer */ + p_msg->len = (UINT16)(p - p_start); + + + L2CA_DataWrite (p_ccb->connection_id, p_msg); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } + else + { + sdp_disconnect (p_ccb, SDP_SUCCESS); + return; + } +} + + +/******************************************************************************* +** +** Function process_service_search_attr_rsp +** +** Description This function is called when there is a search attribute +** response from the server. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply, UINT16 len) +{ + UINT8 *p, *p_start, *p_end, *p_param_len; + UINT8 type; + UINT32 seq_len; + UINT16 param_len, lists_byte_count = 0; + BOOLEAN cont_request_needed = FALSE; + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("process_service_search_attr_rsp len:%d", len); +#endif + /* If p_reply is NULL, we were called for the initial read */ + if (p_reply) + { +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING4("ID & len: 0x%02x-%02x-%02x-%02x", + p_reply[0], p_reply[1], p_reply[2], p_reply[3]); +#endif + /* Skip transaction ID and length */ + p_reply += 4; + + BE_STREAM_TO_UINT16 (lists_byte_count, p_reply); +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("lists_byte_count:%d", lists_byte_count); +#endif + + /* Copy the response to the scratchpad. First, a safety check on the length */ + if ((p_ccb->list_len + lists_byte_count) > SDP_MAX_LIST_BYTE_COUNT) + { + sdp_disconnect (p_ccb, SDP_INVALID_PDU_SIZE); + return; + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING2("list_len: %d, list_byte_count: %d", + p_ccb->list_len, lists_byte_count); +#endif + if (p_ccb->rsp_list == NULL) + { + p_ccb->rsp_list = (UINT8 *)GKI_getbuf (SDP_MAX_LIST_BYTE_COUNT); + if (p_ccb->rsp_list == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no gki buf to save rsp"); + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + } + memcpy (&p_ccb->rsp_list[p_ccb->list_len], p_reply, lists_byte_count); + p_ccb->list_len += lists_byte_count; + p_reply += lists_byte_count; +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("list_len: %d(search_attr_rsp)", p_ccb->list_len); + + /* Check if we need to request a continuation */ + SDP_TRACE_WARNING2("*p_reply:%d(%d)", *p_reply, SDP_MAX_CONTINUATION_LEN); +#endif + if (*p_reply) + { + if (*p_reply > SDP_MAX_CONTINUATION_LEN) + { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + + cont_request_needed = TRUE; + } + } + +#if (SDP_DEBUG_RAW == TRUE) + SDP_TRACE_WARNING1("cont_request_needed:%d", cont_request_needed); +#endif + /* If continuation request (or first time request) */ + if ((cont_request_needed) || (!p_reply)) + { + BT_HDR *p_msg = (BT_HDR *) GKI_getpoolbuf (SDP_POOL_ID); + UINT8 *p; + + if (!p_msg) + { + sdp_disconnect (p_ccb, SDP_NO_RESOURCES); + return; + } + + p_msg->offset = L2CAP_MIN_OFFSET; + p = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + /* Build a service search request packet */ + UINT8_TO_BE_STREAM (p, SDP_PDU_SERVICE_SEARCH_ATTR_REQ); + UINT16_TO_BE_STREAM (p, p_ccb->transaction_id); + p_ccb->transaction_id++; + + /* Skip the length, we need to add it at the end */ + p_param_len = p; + p += 2; + + /* Build the UID sequence. */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + p = sdpu_build_uuid_seq (p, 1, &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx]); +#else + p = sdpu_build_uuid_seq (p, p_ccb->p_db->num_uuid_filters, p_ccb->p_db->uuid_filters); +#endif + + /* Max attribute byte count */ + UINT16_TO_BE_STREAM (p, sdp_cb.max_attr_list_size); + + /* If no attribute filters, build a wildcard attribute sequence */ + if (p_ccb->p_db->num_attr_filters) + p = sdpu_build_attrib_seq (p, p_ccb->p_db->attr_filters, p_ccb->p_db->num_attr_filters); + else + p = sdpu_build_attrib_seq (p, NULL, 0); + + /* No continuation for first request */ + if (p_reply) + { + memcpy (p, p_reply, *p_reply + 1); + p += *p_reply + 1; + } + else + UINT8_TO_BE_STREAM (p, 0); + + /* Go back and put the parameter length into the buffer */ + param_len = p - p_param_len - 2; + UINT16_TO_BE_STREAM (p_param_len, param_len); + + /* Set the length of the SDP data in the buffer */ + p_msg->len = p - p_start; + + + L2CA_DataWrite (p_ccb->connection_id, p_msg); + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + + return; + } + + + /*******************************************************************/ + /* We now have the full response, which is a sequence of sequences */ + /*******************************************************************/ + +#if (SDP_RAW_DATA_INCLUDED == TRUE) + SDP_TRACE_WARNING0("process_service_search_attr_rsp"); + sdp_copy_raw_data (p_ccb, len, TRUE); +#endif + + p = &p_ccb->rsp_list[0]; + + /* The contents is a sequence of attribute sequences */ + type = *p++; + + if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE) + { + SDP_TRACE_WARNING1 ("SDP - Wrong type: 0x%02x in attr_rsp", type); + return; + } + p = sdpu_get_len_from_type (p, type, &seq_len); + + p_end = &p_ccb->rsp_list[p_ccb->list_len]; + + if ((p + seq_len) != p_end) + { + sdp_disconnect (p_ccb, SDP_INVALID_CONT_STATE); + return; + } + + while (p < p_end) + { + p = save_attr_seq (p_ccb, p, &p_ccb->rsp_list[p_ccb->list_len]); + if (!p) + { + sdp_disconnect (p_ccb, SDP_DB_FULL); + return; + } + } + + /* Since we got everything we need, disconnect the call */ + sdp_disconnect (p_ccb, SDP_SUCCESS); +} + +/******************************************************************************* +** +** Function save_attr_seq +** +** Description This function is called when there is a response from +** the server. +** +** Returns pointer to next byte or NULL if error +** +*******************************************************************************/ +static UINT8 *save_attr_seq (tCONN_CB *p_ccb, UINT8 *p, UINT8 *p_msg_end) +{ + UINT32 seq_len, attr_len; + UINT16 attr_id; + UINT8 type, *p_seq_end; + tSDP_DISC_REC *p_rec; + + type = *p++; + + if ((type >> 3) != DATA_ELE_SEQ_DESC_TYPE) + { + SDP_TRACE_WARNING1 ("SDP - Wrong type: 0x%02x in attr_rsp", type); + return (NULL); + } + + p = sdpu_get_len_from_type (p, type, &seq_len); + if ((p + seq_len) > p_msg_end) + { + SDP_TRACE_WARNING1 ("SDP - Bad len in attr_rsp %d", seq_len); + return (NULL); + } + + /* Create a record */ + p_rec = add_record (p_ccb->p_db, p_ccb->device_address); + if (!p_rec) + { + SDP_TRACE_WARNING0 ("SDP - DB full add_record"); + return (NULL); + } + + p_seq_end = p + seq_len; + + while (p < p_seq_end) + { + /* First get the attribute ID */ + type = *p++; + p = sdpu_get_len_from_type (p, type, &attr_len); + if (((type >> 3) != UINT_DESC_TYPE) || (attr_len != 2)) + { + SDP_TRACE_WARNING2 ("SDP - Bad type: 0x%02x or len: %d in attr_rsp", type, attr_len); + return (NULL); + } + BE_STREAM_TO_UINT16 (attr_id, p); + + /* Now, add the attribute value */ + p = add_attr (p, p_ccb->p_db, p_rec, attr_id, NULL, 0); + + if (!p) + { + SDP_TRACE_WARNING0 ("SDP - DB full add_attr"); + return (NULL); + } + } + + return (p); +} + + +/******************************************************************************* +** +** Function add_record +** +** Description This function allocates space for a record from the DB. +** +** Returns pointer to next byte in data stream +** +*******************************************************************************/ +tSDP_DISC_REC *add_record (tSDP_DISCOVERY_DB *p_db, BD_ADDR p_bda) +{ + tSDP_DISC_REC *p_rec; + + /* See if there is enough space in the database */ + if (p_db->mem_free < sizeof (tSDP_DISC_REC)) + return (NULL); + + p_rec = (tSDP_DISC_REC *) p_db->p_free_mem; + p_db->p_free_mem += sizeof (tSDP_DISC_REC); + p_db->mem_free -= sizeof (tSDP_DISC_REC); + + p_rec->p_first_attr = NULL; + p_rec->p_next_rec = NULL; + + memcpy (p_rec->remote_bd_addr, p_bda, BD_ADDR_LEN); + + /* Add the record to the end of chain */ + if (!p_db->p_first_rec) + p_db->p_first_rec = p_rec; + else + { + tSDP_DISC_REC *p_rec1 = p_db->p_first_rec; + + while (p_rec1->p_next_rec) + p_rec1 = p_rec1->p_next_rec; + + p_rec1->p_next_rec = p_rec; + } + + return (p_rec); +} + +#define SDP_ADDITIONAL_LIST_MASK 0x80 +/******************************************************************************* +** +** Function add_attr +** +** Description This function allocates space for an attribute from the DB +** and copies the data into it. +** +** Returns pointer to next byte in data stream +** +*******************************************************************************/ +static UINT8 *add_attr (UINT8 *p, tSDP_DISCOVERY_DB *p_db, tSDP_DISC_REC *p_rec, + UINT16 attr_id, tSDP_DISC_ATTR *p_parent_attr, UINT8 nest_level) +{ + tSDP_DISC_ATTR *p_attr; + UINT32 attr_len; + UINT32 total_len; + UINT16 attr_type; + UINT16 id; + UINT8 type; + UINT8 *p_end; + UINT8 is_additional_list = nest_level & SDP_ADDITIONAL_LIST_MASK; + + nest_level &= ~(SDP_ADDITIONAL_LIST_MASK); + + type = *p++; + p = sdpu_get_len_from_type (p, type, &attr_len); + + attr_len &= SDP_DISC_ATTR_LEN_MASK; + attr_type = (type >> 3) & 0x0f; + + /* See if there is enough space in the database */ + if (attr_len > 4) + total_len = attr_len - 4 + (UINT16)sizeof (tSDP_DISC_ATTR); + else + total_len = sizeof (tSDP_DISC_ATTR); + + /* Ensure it is a multiple of 4 */ + total_len = (total_len + 3) & ~3; + + /* See if there is enough space in the database */ + if (p_db->mem_free < total_len) + return (NULL); + + p_attr = (tSDP_DISC_ATTR *) p_db->p_free_mem; + p_attr->attr_id = attr_id; + p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12); + p_attr->p_next_attr = NULL; + + /* Store the attribute value */ + switch (attr_type) + { + case UINT_DESC_TYPE: + if( (is_additional_list != 0) && (attr_len == 2) ) + { + BE_STREAM_TO_UINT16 (id, p); + if(id != ATTR_ID_PROTOCOL_DESC_LIST) + p -= 2; + else + { + /* Reserve the memory for the attribute now, as we need to add sub-attributes */ + p_db->p_free_mem += sizeof (tSDP_DISC_ATTR); + p_db->mem_free -= sizeof (tSDP_DISC_ATTR); + p_end = p + attr_len; + total_len = 0; + + /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:%d(list)", nest_level); */ + if (nest_level >= MAX_NEST_LEVELS) + { + SDP_TRACE_ERROR0 ("SDP - attr nesting too deep"); + return (p_end); + } + + /* Now, add the list entry */ + p = add_attr (p, p_db, p_rec, ATTR_ID_PROTOCOL_DESC_LIST, p_attr, (UINT8)(nest_level + 1)); + + break; + } + } + /* Case falls through */ + + case TWO_COMP_INT_DESC_TYPE: + switch (attr_len) + { + case 1: + p_attr->attr_value.v.u8 = *p++; + break; + case 2: + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + break; + case 4: + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + break; + default: + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + break; + } + break; + + case UUID_DESC_TYPE: + switch (attr_len) + { + case 2: + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + break; + case 4: + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + if (p_attr->attr_value.v.u32 < 0x10000) + { + attr_len = 2; + p_attr->attr_len_type = (UINT16)attr_len | (attr_type << 12); + p_attr->attr_value.v.u16 = (UINT16) p_attr->attr_value.v.u32; + + } + break; + case 16: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + if (sdpu_is_base_uuid (p)) + { + if ((p[0] == 0) && (p[1] == 0)) + { + p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 2; + p += 2; + BE_STREAM_TO_UINT16 (p_attr->attr_value.v.u16, p); + p += MAX_UUID_SIZE - 4; + } + else + { + p_attr->attr_len_type = (p_attr->attr_len_type & ~SDP_DISC_ATTR_LEN_MASK) | 4; + BE_STREAM_TO_UINT32 (p_attr->attr_value.v.u32, p); + p += MAX_UUID_SIZE - 4; + } + } + else + { + /* coverity[overrun-local] */ + /* + Event overrun-local: Overrun of static array "p_attr->attr_value.v.array" of size 4 at position 15 with index variable "ijk" + False-positive: SDP uses scratch buffer to hold the attribute value. + The actual size of tSDP_DISC_ATVAL does not matter. + If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily + */ + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + } + break; + default: + SDP_TRACE_WARNING1 ("SDP - bad len in UUID attr: %d", attr_len); + return (p + attr_len); + } + break; + + case DATA_ELE_SEQ_DESC_TYPE: + case DATA_ELE_ALT_DESC_TYPE: + /* Reserve the memory for the attribute now, as we need to add sub-attributes */ + p_db->p_free_mem += sizeof (tSDP_DISC_ATTR); + p_db->mem_free -= sizeof (tSDP_DISC_ATTR); + p_end = p + attr_len; + total_len = 0; + + /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:%d", nest_level); */ + if (nest_level >= MAX_NEST_LEVELS) + { + SDP_TRACE_ERROR0 ("SDP - attr nesting too deep"); + return (p_end); + } + if(is_additional_list != 0 || attr_id == ATTR_ID_ADDITION_PROTO_DESC_LISTS) + nest_level |= SDP_ADDITIONAL_LIST_MASK; + /* SDP_TRACE_DEBUG1 ("SDP - attr nest level:0x%x(finish)", nest_level); */ + + while (p < p_end) + { + /* Now, add the list entry */ + p = add_attr (p, p_db, p_rec, 0, p_attr, (UINT8)(nest_level + 1)); + + if (!p) + return (NULL); + } + break; + + case TEXT_STR_DESC_TYPE: + case URL_DESC_TYPE: + BE_STREAM_TO_ARRAY (p, p_attr->attr_value.v.array, (INT32)attr_len); + break; + + case BOOLEAN_DESC_TYPE: + switch (attr_len) + { + case 1: + p_attr->attr_value.v.u8 = *p++; + break; + default: + SDP_TRACE_WARNING1 ("SDP - bad len in boolean attr: %d", attr_len); + return (p + attr_len); + } + break; + + default: /* switch (attr_type) */ + break; + } + + p_db->p_free_mem += total_len; + p_db->mem_free -= total_len; + + /* Add the attribute to the end of the chain */ + if (!p_parent_attr) + { + if (!p_rec->p_first_attr) + p_rec->p_first_attr = p_attr; + else + { + tSDP_DISC_ATTR *p_attr1 = p_rec->p_first_attr; + + while (p_attr1->p_next_attr) + p_attr1 = p_attr1->p_next_attr; + + p_attr1->p_next_attr = p_attr; + } + } + else + { + if (!p_parent_attr->attr_value.v.p_sub_attr) + { + p_parent_attr->attr_value.v.p_sub_attr = p_attr; + /* SDP_TRACE_DEBUG4 ("parent:0x%x(id:%d), ch:0x%x(id:%d)", + p_parent_attr, p_parent_attr->attr_id, p_attr, p_attr->attr_id); */ + } + else + { + tSDP_DISC_ATTR *p_attr1 = p_parent_attr->attr_value.v.p_sub_attr; + /* SDP_TRACE_DEBUG4 ("parent:0x%x(id:%d), ch1:0x%x(id:%d)", + p_parent_attr, p_parent_attr->attr_id, p_attr1, p_attr1->attr_id); */ + + while (p_attr1->p_next_attr) + p_attr1 = p_attr1->p_next_attr; + + p_attr1->p_next_attr = p_attr; + /* SDP_TRACE_DEBUG2 ("new ch:0x%x(id:%d)", p_attr, p_attr->attr_id); */ + } + } + + return (p); +} + +#endif /* CLIENT_ENABLED == TRUE */ diff --git a/stack/sdp/sdp_main.c b/stack/sdp/sdp_main.c new file mode 100644 index 0000000..14cd39b --- /dev/null +++ b/stack/sdp/sdp_main.c @@ -0,0 +1,724 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the main SDP functions + * + ******************************************************************************/ + +#include +#include +#include + +#include "bt_target.h" +#include "gki.h" +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "l2c_api.h" +#include "l2cdefs.h" + +#include "btu.h" +#include "btm_api.h" + +#include "sdp_api.h" +#include "sdpint.h" + + +/********************************************************************************/ +/* G L O B A L S D P D A T A */ +/********************************************************************************/ +#if SDP_DYNAMIC_MEMORY == FALSE +tSDP_CB sdp_cb; +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, + UINT8 l2cap_id); +static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg); + +#if SDP_CLIENT_ENABLED == TRUE +static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result); +static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result); +#else +#define sdp_connect_cfm NULL +#define sdp_disconnect_cfm NULL +#endif + + +/******************************************************************************* +** +** Function sdp_init +** +** Description This function initializes the SDP unit. +** +** Returns void +** +*******************************************************************************/ +void sdp_init (void) +{ + /* Clears all structures and local SDP database (if Server is enabled) */ + memset (&sdp_cb, 0, sizeof (tSDP_CB)); + + /* Initialize the L2CAP configuration. We only care about MTU and flush */ + sdp_cb.l2cap_my_cfg.mtu_present = TRUE; + sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE; + sdp_cb.l2cap_my_cfg.flush_to_present = TRUE; + sdp_cb.l2cap_my_cfg.flush_to = SDP_FLUSH_TO; + + sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16; + sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS; + +#if SDP_SERVER_ENABLED == TRUE + /* Register with Security Manager for the specific security level */ + if (!BTM_SetSecurityLevel (FALSE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER, + SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) + { + SDP_TRACE_ERROR0 ("Security Registration Server failed"); + return; + } +#endif + +#if SDP_CLIENT_ENABLED == TRUE + /* Register with Security Manager for the specific security level */ + if (!BTM_SetSecurityLevel (TRUE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER, + SDP_SECURITY_LEVEL, SDP_PSM, 0, 0)) + { + SDP_TRACE_ERROR0 ("Security Registration for Client failed"); + return; + } +#endif + +#if defined(SDP_INITIAL_TRACE_LEVEL) + sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL; +#else + sdp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind; + sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm; + sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL; + sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind; + sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm; + sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind; + sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm; + sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL; + sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind; + sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL; + sdp_cb.reg_info.pL2CA_TxComplete_Cb = NULL; + + /* Now, register with L2CAP */ + if (!L2CA_Register (SDP_PSM, &sdp_cb.reg_info)) + { + SDP_TRACE_ERROR0 ("SDP Registration failed"); + } +} + +#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE) +/******************************************************************************* +** +** Function sdp_set_max_attr_list_size +** +** Description This function sets the max attribute list size to use +** +** Returns void +** +*******************************************************************************/ +UINT16 sdp_set_max_attr_list_size (UINT16 max_size) +{ + if (max_size > (sdp_cb.l2cap_my_cfg.mtu - 16) ) + max_size = sdp_cb.l2cap_my_cfg.mtu - 16; + + sdp_cb.max_attr_list_size = max_size; + + return sdp_cb.max_attr_list_size; +} +#endif + +/******************************************************************************* +** +** Function sdp_connect_ind +** +** Description This function handles an inbound connection indication +** from L2CAP. This is the case where we are acting as a +** server. +** +** Returns void +** +*******************************************************************************/ +static void sdp_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) +{ +#if SDP_SERVER_ENABLED == TRUE + tCONN_CB *p_ccb; + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = sdpu_allocate_ccb()) == NULL) + return; + + /* Transition to the next appropriate state, waiting for config setup. */ + p_ccb->con_state = SDP_STATE_CFG_SETUP; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->device_address[0], bd_addr, sizeof (BD_ADDR)); + p_ccb->connection_id = l2cap_cid; + + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + { + tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg; + + if (cfg.fcr_present) + { + SDP_TRACE_DEBUG6("sdp_connect_ind: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u", + cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit, + cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps); + } + + if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present + && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + /* FCR not desired; try again in basic mode */ + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + cfg.fcr_present = FALSE; + L2CA_ConfigReq (l2cap_cid, &cfg); + } + } + + SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP conn ind, sent config req, CID 0x%x", p_ccb->connection_id); +#else /* No server */ + /* Reject the connection */ + L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_PSM, 0); +#endif +} + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_connect_cfm +** +** Description This function handles the connect confirm events +** from L2CAP. This is the case when we are acting as a +** client and have sent a connect request. +** +** Returns void +** +*******************************************************************************/ +static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tCONN_CB *p_ccb; + tL2CAP_CFG_INFO cfg; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid); + return; + } + + /* If the connection response contains success status, then */ + /* Transition to the next state and startup the timer. */ + if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP)) + { + p_ccb->con_state = SDP_STATE_CFG_SETUP; + + cfg = sdp_cb.l2cap_my_cfg; + + if (cfg.fcr_present) + { + SDP_TRACE_DEBUG6("sdp_connect_cfm: mode %u, txwinsz %u, max_trans %u, rtrans_tout %u, mon_tout %u, mps %u", + cfg.fcr.mode, cfg.fcr.tx_win_sz, cfg.fcr.max_transmit, + cfg.fcr.rtrans_tout,cfg.fcr.mon_tout, cfg.fcr.mps); + } + + if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present + && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) + { + /* FCR not desired; try again in basic mode */ + cfg.fcr_present = FALSE; + cfg.fcr.mode = L2CAP_FCR_BASIC_MODE; + L2CA_ConfigReq (l2cap_cid, &cfg); + } + + SDP_TRACE_EVENT1 ("SDP - got conn cnf, sent cfg req, CID: 0x%x", p_ccb->connection_id); + } + else + { + SDP_TRACE_WARNING2 ("SDP - Rcvd conn cnf with error: 0x%x CID 0x%x", result, p_ccb->connection_id); + + /* Tell the user if he has a callback */ + if (p_ccb->p_cb || p_ccb->p_cb2) + { + UINT16 err = -1; + if ((result == HCI_ERR_HOST_REJECT_SECURITY) + || (result == HCI_ERR_AUTH_FAILURE) + || (result == HCI_ERR_PAIRING_NOT_ALLOWED) + || (result == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) + || (result == HCI_ERR_KEY_MISSING)) + err = SDP_SECURITY_ERR; + else if (result == HCI_ERR_HOST_REJECT_DEVICE) + err = SDP_CONN_REJECTED; + else + err = SDP_CONN_FAILED; + if(p_ccb->p_cb) + (*p_ccb->p_cb)(err); + else if(p_ccb->p_cb2) + (*p_ccb->p_cb2)(err, p_ccb->user_data); + + } + sdpu_release_ccb (p_ccb); + } +} +#endif /* SDP_CLIENT_ENABLED == TRUE */ + + +/******************************************************************************* +** +** Function sdp_config_ind +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void sdp_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* Remember the remote MTU size */ + if (!p_cfg->mtu_present) + { + /* use min(L2CAP_DEFAULT_MTU,SDP_MTU_SIZE) for GKI buffer size reasons */ + p_ccb->rem_mtu_size = (L2CAP_DEFAULT_MTU > SDP_MTU_SIZE)?SDP_MTU_SIZE:L2CAP_DEFAULT_MTU; + } + else + { + if (p_cfg->mtu > SDP_MTU_SIZE) + p_ccb->rem_mtu_size = SDP_MTU_SIZE; + else + p_ccb->rem_mtu_size = p_cfg->mtu; + } + + /* For now, always accept configuration from the other side */ + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + + /* Check peer config request against our rfcomm configuration */ + if (p_cfg->fcr_present) + { + /* Reject the window size if it is bigger than we want it to be */ + if (p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) + { + if (sdp_cb.l2cap_my_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE + && p_cfg->fcr.tx_win_sz > sdp_cb.l2cap_my_cfg.fcr.tx_win_sz) + { + p_cfg->fcr.tx_win_sz = sdp_cb.l2cap_my_cfg.fcr.tx_win_sz; + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with SMALLER TX WINDOW"); + } + + /* Reject if locally we want basic and they don't */ + if (sdp_cb.l2cap_my_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) + { + /* Ask for a new setup */ + p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE; + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + SDP_TRACE_DEBUG0("sdp_config_ind(CONFIG) -> Please try again with BASIC mode"); + } + /* Remain in configure state and give the peer our desired configuration */ + if (p_cfg->result != L2CAP_CFG_OK) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd cfg ind, Unacceptable Parameters sent cfg cfm, CID: 0x%x", l2cap_cid); + L2CA_ConfigRsp (l2cap_cid, p_cfg); + return; + } + } + else /* We agree with peer's request */ + p_cfg->fcr_present = FALSE; + } + + L2CA_ConfigRsp (l2cap_cid, p_cfg); + + SDP_TRACE_EVENT1 ("SDP - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); + + p_ccb->con_flags |= SDP_FLAGS_HIS_CFG_DONE; + + if (p_ccb->con_flags & SDP_FLAGS_MY_CFG_DONE) + { + p_ccb->con_state = SDP_STATE_CONNECTED; + + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) + sdp_disc_connected (p_ccb); + else + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } + +} + + +/******************************************************************************* +** +** Function sdp_config_cfm +** +** Description This function processes the L2CAP configuration confirmation +** event. +** +** Returns void +** +*******************************************************************************/ +static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) +{ + tCONN_CB *p_ccb; + + SDP_TRACE_EVENT2 ("SDP - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + return; + } + + /* For now, always accept configuration from the other side */ + if (p_cfg->result == L2CAP_CFG_OK) + { + p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE; + + if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE) + { + p_ccb->con_state = SDP_STATE_CONNECTED; + + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) + sdp_disc_connected (p_ccb); + else + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + } + } + else + { + /* If peer has rejected FCR and suggested basic then try basic */ + if (p_cfg->fcr_present) + { + tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg; + cfg.fcr_present = FALSE; + L2CA_ConfigReq (l2cap_cid, &cfg); + + /* Remain in configure state */ + return; + } + +#if SDP_CLIENT_ENABLED == TRUE + sdp_disconnect(p_ccb, SDP_CFG_FAILED); +#endif + } +} + +/******************************************************************************* +** +** Function sdp_disconnect_ind +** +** Description This function handles a disconnect event from L2CAP. If +** requested to, we ack the disconnect before dropping the CCB +** +** Returns void +** +*******************************************************************************/ +static void sdp_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + return; + } + + if (ack_needed) + L2CA_DisconnectRsp (l2cap_cid); + + SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); +#if SDP_CLIENT_ENABLED == TRUE + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) + (*p_ccb->p_cb) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ? + SDP_SUCCESS : SDP_CONN_FAILED)); + else if (p_ccb->p_cb2) + (*p_ccb->p_cb2) ((UINT16) ((p_ccb->con_state == SDP_STATE_CONNECTED) ? + SDP_SUCCESS : SDP_CONN_FAILED), p_ccb->user_data); + +#endif + sdpu_release_ccb (p_ccb); +} + +/******************************************************************************* +** +** Function sdp_data_ind +** +** Description This function is called when data is received from L2CAP. +** if we are the originator of the connection, we are the SDP +** client, and the received message is queued up for the client. +** +** If we are the destination of the connection, we are the SDP +** server, so the message is passed to the server processing +** function. +** +** Returns void +** +*******************************************************************************/ +static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL) + { + if (p_ccb->con_state == SDP_STATE_CONNECTED) + { + if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) + sdp_disc_server_rsp (p_ccb, p_msg); + else + sdp_server_handle_client_req (p_ccb, p_msg); + } + else + { + SDP_TRACE_WARNING2 ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x", + p_ccb->con_state, l2cap_cid); + } + } + else + { + SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + } + + GKI_freebuf (p_msg); +} + + +#if SDP_CLIENT_ENABLED == TRUE +/******************************************************************************* +** +** Function sdp_conn_originate +** +** Description This function is called from the API to originate a +** connection. +** +** Returns void +** +*******************************************************************************/ +tCONN_CB* sdp_conn_originate (UINT8 *p_bd_addr) +{ + tCONN_CB *p_ccb; + UINT16 cid; + + /* Allocate a new CCB. Return if none available. */ + if ((p_ccb = sdpu_allocate_ccb()) == NULL) + { + SDP_TRACE_WARNING0 ("SDP - no spare CCB for orig"); + return (NULL); + } + + SDP_TRACE_EVENT0 ("SDP - Originate started"); + + /* We are the originator of this connection */ + p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; + + /* Save the BD Address and Channel ID. */ + memcpy (&p_ccb->device_address[0], p_bd_addr, sizeof (BD_ADDR)); + + /* Transition to the next appropriate state, waiting for connection confirm. */ + p_ccb->con_state = SDP_STATE_CONN_SETUP; + +// btla-specific ++ +#ifndef ANDROID_APP_INCLUDED /* Skip for Android: Do not need to set out_service for sdp, since sdp does not use sec. Prevents over-writing service_rec of a connection already in progress */ + BTM_SetOutService(p_bd_addr, BTM_SEC_SERVICE_SDP_SERVER, 0); +#endif +// btla-specific -- + + cid = L2CA_ConnectReq (SDP_PSM, p_bd_addr); + + /* Check if L2CAP started the connection process */ + if (cid != 0) + { + p_ccb->connection_id = cid; + + return (p_ccb); + } + else + { + SDP_TRACE_WARNING0 ("SDP - Originate failed"); + sdpu_release_ccb (p_ccb); + return (NULL); + } +} + +/******************************************************************************* +** +** Function sdp_disconnect +** +** Description This function disconnects a connection. +** +** Returns void +** +*******************************************************************************/ +void sdp_disconnect (tCONN_CB*p_ccb, UINT16 reason) +{ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + + /* If we are browsing for multiple UUIDs ... */ + if ((p_ccb->con_state == SDP_STATE_CONNECTED) + && (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) + && ((reason == SDP_SUCCESS) || (reason == SDP_NO_RECS_MATCH))) + { + /* If the browse found something, do no more searching */ + if ((p_ccb->cur_uuid_idx == 0) && (p_ccb->p_db->p_first_rec)) + p_ccb->cur_uuid_idx = p_ccb->p_db->num_uuid_filters; + + while (++p_ccb->cur_uuid_idx < p_ccb->p_db->num_uuid_filters) + { + /* Check we have not already found the UUID (maybe through browse) */ + if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len == 2) + && (SDP_FindServiceInDb (p_ccb->p_db, + p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].uu.uuid16, + NULL))) + continue; + + if ((p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx].len > 2) + && (SDP_FindServiceUUIDInDb (p_ccb->p_db, + &p_ccb->p_db->uuid_filters[p_ccb->cur_uuid_idx], NULL))) + continue; + + p_ccb->cur_handle = 0; + + SDP_TRACE_EVENT1 ("SDP - looking for for more, CID: 0x%x", + p_ccb->connection_id); + + sdp_disc_connected (p_ccb); + return; + } + } + + if ((reason == SDP_NO_RECS_MATCH) && (p_ccb->p_db->p_first_rec)) + reason = SDP_SUCCESS; + +#endif + + SDP_TRACE_EVENT1 ("SDP - disconnect CID: 0x%x", p_ccb->connection_id); + + /* Check if we have a connection ID */ + if (p_ccb->connection_id != 0) + { + L2CA_DisconnectReq (p_ccb->connection_id); + p_ccb->disconnect_reason = reason; + } + + /* If at setup state, we may not get callback ind from L2CAP */ + /* Call user callback immediately */ + if (p_ccb->con_state == SDP_STATE_CONN_SETUP) + { + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) + (*p_ccb->p_cb) (reason); + else if (p_ccb->p_cb2) + (*p_ccb->p_cb2) (reason, p_ccb->user_data); + + sdpu_release_ccb (p_ccb); + } + +} + +/******************************************************************************* +** +** Function sdp_disconnect_cfm +** +** Description This function handles a disconnect confirm event from L2CAP. +** +** Returns void +** +*******************************************************************************/ +static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) +{ + tCONN_CB *p_ccb; + + /* Find CCB based on CID */ + if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL) + { + SDP_TRACE_WARNING1 ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + return; + } + + SDP_TRACE_EVENT1 ("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); + + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) + (*p_ccb->p_cb) (p_ccb->disconnect_reason); + else if (p_ccb->p_cb2) + (*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data); + + + sdpu_release_ccb (p_ccb); +} + +#endif /* SDP_CLIENT_ENABLED == TRUE */ + +/******************************************************************************* +** +** Function sdp_conn_timeout +** +** Description This function processes a timeout. Currently, we simply send +** a disconnect request to L2CAP. +** +** Returns void +** +*******************************************************************************/ +void sdp_conn_timeout (tCONN_CB*p_ccb) +{ + SDP_TRACE_EVENT2 ("SDP - CCB timeout in state: %d CID: 0x%x", + p_ccb->con_state, p_ccb->connection_id); + + L2CA_DisconnectReq (p_ccb->connection_id); +#if SDP_CLIENT_ENABLED == TRUE + /* Tell the user if he has a callback */ + if (p_ccb->p_cb) + (*p_ccb->p_cb) (SDP_CONN_FAILED); + else if (p_ccb->p_cb2) + (*p_ccb->p_cb2) (SDP_CONN_FAILED, p_ccb->user_data); +#endif + sdpu_release_ccb (p_ccb); +} + + + + diff --git a/stack/sdp/sdp_server.c b/stack/sdp/sdp_server.c new file mode 100644 index 0000000..5736b8d --- /dev/null +++ b/stack/sdp/sdp_server.c @@ -0,0 +1,835 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle the SDP server functions. + * This is mainly dealing with client requests + * + ******************************************************************************/ + +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" +#include "btu.h" + +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "sdp_api.h" +#include "sdpint.h" + + +#if SDP_SERVER_ENABLED == TRUE + +/* Maximum number of bytes to reserve out of SDP MTU for response data */ +#define SDP_MAX_SERVICE_RSPHDR_LEN 12 +#define SDP_MAX_SERVATTR_RSPHDR_LEN 10 +#define SDP_MAX_ATTR_RSPHDR_LEN 10 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + +static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + +static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end); + + +/********************************************************************************/ +/* E R R O R T E X T S T R I N G S */ +/* */ +/* The default is to have no text string, but we allow the strings to be */ +/* configured in target.h if people want them. */ +/********************************************************************************/ +#ifndef SDP_TEXT_BAD_HEADER +#define SDP_TEXT_BAD_HEADER NULL +#endif + +#ifndef SDP_TEXT_BAD_PDU +#define SDP_TEXT_BAD_PDU NULL +#endif + +#ifndef SDP_TEXT_BAD_UUID_LIST +#define SDP_TEXT_BAD_UUID_LIST NULL +#endif + +#ifndef SDP_TEXT_BAD_HANDLE +#define SDP_TEXT_BAD_HANDLE NULL +#endif + +#ifndef SDP_TEXT_BAD_ATTR_LIST +#define SDP_TEXT_BAD_ATTR_LIST NULL +#endif + +#ifndef SDP_TEXT_BAD_CONT_LEN +#define SDP_TEXT_BAD_CONT_LEN NULL +#endif + +#ifndef SDP_TEXT_BAD_CONT_INX +#define SDP_TEXT_BAD_CONT_INX NULL +#endif + +/******************************************************************************* +** +** Function sdp_server_handle_client_req +** +** Description This is the main dispatcher of the SDP server. It is called +** when any data is received from L2CAP, and dispatches the +** request to the appropriate handler. +** +** Returns void +** +*******************************************************************************/ +void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg) +{ + UINT8 *p_req = (UINT8 *) (p_msg + 1) + p_msg->offset; + UINT8 *p_req_end = p_req + p_msg->len; + UINT8 pdu_id; + UINT16 trans_num, param_len; + + + /* Start inactivity timer */ + btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT); + + /* The first byte in the message is the pdu type */ + pdu_id = *p_req++; + + /* Extract the transaction number and parameter length */ + BE_STREAM_TO_UINT16 (trans_num, p_req); + BE_STREAM_TO_UINT16 (param_len, p_req); + + if ((p_req + param_len) > p_req_end) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_PDU_SIZE, SDP_TEXT_BAD_HEADER); + return; + } + + switch (pdu_id) + { + case SDP_PDU_SERVICE_SEARCH_REQ: + process_service_search (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + case SDP_PDU_SERVICE_ATTR_REQ: + process_service_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + case SDP_PDU_SERVICE_SEARCH_ATTR_REQ: + process_service_search_attr_req (p_ccb, trans_num, param_len, p_req, p_req_end); + break; + + default: + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_PDU); + SDP_TRACE_WARNING1 ("SDP - server got unknown PDU: 0x%x", pdu_id); + break; + } +} + + + +/******************************************************************************* +** +** Function process_service_search +** +** Description This function handles a service search request from the +** client. It builds a reply message with info from the database, +** and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_replies, cur_handles, rem_handles, cont_offset; + tSDP_UUID_SEQ uid_seq; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, num_rsp_handles, xx; + UINT32 rsp_handles[SDP_MAX_RECORDS]; + tSDP_RECORD *p_rec = NULL; + BT_HDR *p_buf; + BOOLEAN is_cont = FALSE; + + p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); + + if ((!p_req) || (!uid_seq.num_uids)) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); + return; + } + + /* Get the max replies we can send. Cap it at our max anyways. */ + BE_STREAM_TO_UINT16 (max_replies, p_req); + + if (max_replies > SDP_MAX_RECORDS) + max_replies = SDP_MAX_RECORDS; + + /* Get a list of handles that match the UUIDs given to us */ + for (num_rsp_handles = 0; num_rsp_handles < max_replies; ) + { + p_rec = sdp_db_service_search (p_rec, &uid_seq); + + if (p_rec) + rsp_handles[num_rsp_handles++] = p_rec->record_handle; + else + break; + } + + /* Check if this is a continuation request */ + if (*p_req) + { + if (*p_req++ != SDP_CONTINUATION_LEN) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, + SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, + SDP_TEXT_BAD_CONT_INX); + return; + } + + rem_handles = num_rsp_handles - cont_offset; /* extract the remaining handles */ + } + else + { + rem_handles = num_rsp_handles; + cont_offset = 0; + } + + /* Calculate how many handles will fit in one PDU */ + cur_handles = (UINT16)((p_ccb->rem_mtu_size - SDP_MAX_SERVICE_RSPHDR_LEN) / 4); + + if (rem_handles <= cur_handles) + cur_handles = rem_handles; + else /* Continuation is set */ + { + p_ccb->cont_offset += cur_handles; + is_cont = TRUE; + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no buf for search rsp"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the length, we need to add it at the end */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + /* Put in total and current number of handles, and handles themselves */ + UINT16_TO_BE_STREAM (p_rsp, num_rsp_handles); + UINT16_TO_BE_STREAM (p_rsp, cur_handles); + +/* SDP_TRACE_DEBUG5("SDP Service Rsp: tothdl %d, curhdlr %d, start %d, end %d, cont %d", + num_rsp_handles, cur_handles, cont_offset, + cont_offset + cur_handles-1, is_cont); */ + for (xx = cont_offset; xx < cont_offset + cur_handles; xx++) + UINT32_TO_BE_STREAM (p_rsp, rsp_handles[xx]); + + if (is_cont) + { + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } + else + UINT8_TO_BE_STREAM (p_rsp, 0); + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + +/******************************************************************************* +** +** Function process_service_attr_req +** +** Description This function handles an attribute request from the client. +** It builds a reply message with info from the database, +** and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_list_len, len_to_send, cont_offset; + INT16 rem_len; + tSDP_ATTR_SEQ attr_seq, attr_seq_sav; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, xx; + UINT32 rec_handle; + tSDP_RECORD *p_rec; + tSDP_ATTRIBUTE *p_attr; + BT_HDR *p_buf; + BOOLEAN is_cont = FALSE; + UINT16 attr_len; + + /* Extract the record handle */ + BE_STREAM_TO_UINT32 (rec_handle, p_req); + + if (p_req > p_req_end) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); + return; + } + + /* Get the max list length we can send. Cap it at MTU size minus overhead */ + BE_STREAM_TO_UINT16 (max_list_len, p_req); + + if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN)) + max_list_len = p_ccb->rem_mtu_size - SDP_MAX_ATTR_RSPHDR_LEN; + + p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); + + if ((!p_req) || (!attr_seq.num_attr) || (p_req > p_req_end)) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); + return; + } + + memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; + + /* Find a record with the record handle */ + p_rec = sdp_db_find_record (rec_handle); + if (!p_rec) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_SERV_REC_HDL, SDP_TEXT_BAD_HANDLE); + return; + } + + /* Check if this is a continuation request */ + if (*p_req) + { + if (*p_req++ != SDP_CONTINUATION_LEN) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); + return; + } + + if (!p_ccb->rsp_list) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + is_cont = TRUE; + + /* Initialise for continuation response */ + p_rsp = &p_ccb->rsp_list[0]; + attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id; + } + else + { + /* Get a scratch buffer to store response */ + if (!p_ccb->rsp_list) + { + p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len); + if (p_ccb->rsp_list == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp"); + return; + } + } + + p_ccb->cont_offset = 0; + p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ + + /* Reset continuation parameters in p_ccb */ + p_ccb->cont_info.prev_sdp_rec = NULL; + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.attr_offset = 0; + } + + /* Search for attributes that match the list given to us */ + for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) + { + p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); + + if (p_attr) + { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + + /* just in case */ + if (rem_len <= 0) + { + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + break; + } + + attr_len = sdpu_get_attrib_entry_len(p_attr); + /* if there is a partial attribute pending to be sent */ + if (p_ccb->cont_info.attr_offset) + { + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, + &p_ccb->cont_info.attr_offset); + + /* If the partial attrib could not been fully added yet */ + if (p_ccb->cont_info.attr_offset != attr_len) + break; + else /* If the partial attrib has been added in full by now */ + p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ + } + else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ + { + if (attr_len >= MAX_ATTR_LEN) + { + SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + + /* add the partial attribute if possible */ + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, + &p_ccb->cont_info.attr_offset); + + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + break; + } + else /* build the whole attribute */ + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + + /* If doing a range, stick with this one till no more attributes found */ + if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) + { + /* Update for next time through */ + attr_seq.attr_entry[xx].start = p_attr->id + 1; + + xx--; + } + } + } + /* If all the attributes have been accomodated in p_rsp, + reset next_attr_index */ + if (xx == attr_seq.num_attr) + p_ccb->cont_info.next_attr_index = 0; + + len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); + cont_offset = 0; + + if (!is_cont) + { + p_ccb->list_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav) + 3; + /* Put in the sequence header (2 or 3 bytes) */ + if (p_ccb->list_len > 255) + { + p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + } + else + { + cont_offset = 1; + + p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + + p_ccb->list_len--; + len_to_send--; + } + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no buf for search rsp"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_ATTR_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, add it when we know the length */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + UINT16_TO_BE_STREAM (p_rsp, len_to_send); + + memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); + p_rsp += len_to_send; + + p_ccb->cont_offset += len_to_send; + + /* If anything left to send, continuation needed */ + if (p_ccb->cont_offset < p_ccb->list_len) + { + is_cont = TRUE; + + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } + else + UINT8_TO_BE_STREAM (p_rsp, 0); + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + + +/******************************************************************************* +** +** Function process_service_search_attr_req +** +** Description This function handles a combined service search and attribute +** read request from the client. It builds a reply message with +** info from the database, and sends the reply back to the client. +** +** Returns void +** +*******************************************************************************/ +static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, + UINT16 param_len, UINT8 *p_req, + UINT8 *p_req_end) +{ + UINT16 max_list_len; + INT16 rem_len; + UINT16 len_to_send, cont_offset; + tSDP_UUID_SEQ uid_seq; + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len, xx; + tSDP_RECORD *p_rec; + tSDP_ATTR_SEQ attr_seq, attr_seq_sav; + tSDP_ATTRIBUTE *p_attr; + BT_HDR *p_buf; + BOOLEAN maxxed_out = FALSE, is_cont = FALSE; + UINT8 *p_seq_start; + UINT16 seq_len, attr_len; + + /* Extract the UUID sequence to search for */ + p_req = sdpu_extract_uid_seq (p_req, param_len, &uid_seq); + + if ((!p_req) || (!uid_seq.num_uids)) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_UUID_LIST); + return; + } + + /* Get the max list length we can send. Cap it at our max list length. */ + BE_STREAM_TO_UINT16 (max_list_len, p_req); + + if (max_list_len > (p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN)) + max_list_len = p_ccb->rem_mtu_size - SDP_MAX_SERVATTR_RSPHDR_LEN; + + p_req = sdpu_extract_attr_seq (p_req, param_len, &attr_seq); + + if ((!p_req) || (!attr_seq.num_attr)) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_REQ_SYNTAX, SDP_TEXT_BAD_ATTR_LIST); + return; + } + + memcpy(&attr_seq_sav, &attr_seq, sizeof(tSDP_ATTR_SEQ)) ; + + /* Check if this is a continuation request */ + if (*p_req) + { + if (*p_req++ != SDP_CONTINUATION_LEN) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_LEN); + return; + } + BE_STREAM_TO_UINT16 (cont_offset, p_req); + + if (cont_offset != p_ccb->cont_offset) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_INVALID_CONT_STATE, SDP_TEXT_BAD_CONT_INX); + return; + } + + if (!p_ccb->rsp_list) + { + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + is_cont = TRUE; + + /* Initialise for continuation response */ + p_rsp = &p_ccb->rsp_list[0]; + attr_seq.attr_entry[p_ccb->cont_info.next_attr_index].start = p_ccb->cont_info.next_attr_start_id; + } + else + { + /* Get a scratch buffer to store response */ + if (!p_ccb->rsp_list) + { + p_ccb->rsp_list = (UINT8 *)GKI_getbuf (max_list_len); + if (p_ccb->rsp_list == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no scratch buf for search rsp"); + return; + } + } + + p_ccb->cont_offset = 0; + p_rsp = &p_ccb->rsp_list[3]; /* Leave space for data elem descr */ + + /* Reset continuation parameters in p_ccb */ + p_ccb->cont_info.prev_sdp_rec = NULL; + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; + p_ccb->cont_info.attr_offset = 0; + } + + /* Get a list of handles that match the UUIDs given to us */ + for (p_rec = sdp_db_service_search (p_ccb->cont_info.prev_sdp_rec, &uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, &uid_seq)) + { + /* Allow space for attribute sequence type and length */ + p_seq_start = p_rsp; + if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) + { + /* See if there is enough room to include a new service in the current response */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + if (rem_len < 3) + { + /* Not enough room. Update continuation info for next response */ + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.next_attr_start_id = attr_seq.attr_entry[0].start; + break; + } + p_rsp += 3; + } + + /* Get a list of handles that match the UUIDs given to us */ + for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) + { + p_attr = sdp_db_find_attr_in_rec (p_rec, attr_seq.attr_entry[xx].start, attr_seq.attr_entry[xx].end); + + if (p_attr) + { + /* Check if attribute fits. Assume 3-byte value type/length */ + rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); + + /* just in case */ + if (rem_len <= 0) + { + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + maxxed_out = TRUE; + break; + } + + attr_len = sdpu_get_attrib_entry_len(p_attr); + /* if there is a partial attribute pending to be sent */ + if (p_ccb->cont_info.attr_offset) + { + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, rem_len, + &p_ccb->cont_info.attr_offset); + + /* If the partial attrib could not been fully added yet */ + if (p_ccb->cont_info.attr_offset != attr_len) + { + maxxed_out = TRUE; + break; + } + else /* If the partial attrib has been added in full by now */ + p_ccb->cont_info.attr_offset = 0; /* reset attr_offset */ + } + else if (rem_len < attr_len) /* Not enough space for attr... so add partially */ + { + if (attr_len >= MAX_ATTR_LEN) + { + SDP_TRACE_ERROR2("SDP attr too big: max_list_len=%d,attr_len=%d", max_list_len, attr_len); + sdpu_build_n_send_error (p_ccb, trans_num, SDP_NO_RESOURCES, NULL); + return; + } + + /* add the partial attribute if possible */ + p_rsp = sdpu_build_partial_attrib_entry (p_rsp, p_attr, (UINT16)rem_len, + &p_ccb->cont_info.attr_offset); + + p_ccb->cont_info.next_attr_index = xx; + p_ccb->cont_info.next_attr_start_id = p_attr->id; + maxxed_out = TRUE; + break; + } + else /* build the whole attribute */ + p_rsp = sdpu_build_attrib_entry (p_rsp, p_attr); + + /* If doing a range, stick with this one till no more attributes found */ + if (attr_seq.attr_entry[xx].start != attr_seq.attr_entry[xx].end) + { + /* Update for next time through */ + attr_seq.attr_entry[xx].start = p_attr->id + 1; + + xx--; + } + } + } + + /* Go back and put the type and length into the buffer */ + if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) + { + seq_len = sdpu_get_attrib_seq_len(p_rec, &attr_seq_sav); + if (seq_len != 0) + { + UINT8_TO_BE_STREAM (p_seq_start, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_seq_start, seq_len); + + if (maxxed_out) + p_ccb->cont_info.last_attr_seq_desc_sent = TRUE; + } + else + p_rsp = p_seq_start; + } + + if (maxxed_out) + break; + + /* Restore the attr_seq to look for in the next sdp record */ + memcpy(&attr_seq, &attr_seq_sav, sizeof(tSDP_ATTR_SEQ)) ; + + /* Reset the next attr index */ + p_ccb->cont_info.next_attr_index = 0; + p_ccb->cont_info.prev_sdp_rec = p_rec; + p_ccb->cont_info.last_attr_seq_desc_sent = FALSE; + } + + /* response length */ + len_to_send = (UINT16) (p_rsp - &p_ccb->rsp_list[0]); + cont_offset = 0; + + /* If first response, insert sequence header */ + if (!is_cont) + { + /* Get the total list length for requested uid and attribute sequence */ + p_ccb->list_len = sdpu_get_list_len(&uid_seq, &attr_seq_sav) + 3; + /* Put in the sequence header (2 or 3 bytes) */ + if (p_ccb->list_len > 255) + { + p_ccb->rsp_list[0] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + p_ccb->rsp_list[1] = (UINT8) ((p_ccb->list_len - 3) >> 8); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + } + else + { + cont_offset = 1; + + p_ccb->rsp_list[1] = (UINT8) ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + p_ccb->rsp_list[2] = (UINT8) (p_ccb->list_len - 3); + + p_ccb->list_len--; + len_to_send--; + } + } + + /* Get a buffer to use to build the response */ + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no buf for search rsp"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + /* Start building a rsponse */ + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_SERVICE_SEARCH_ATTR_RSP); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, add it when we know the length */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + /* Stream the list length to send */ + UINT16_TO_BE_STREAM (p_rsp, len_to_send); + + /* copy from rsp_list to the actual buffer to be sent */ + memcpy (p_rsp, &p_ccb->rsp_list[cont_offset], len_to_send); + p_rsp += len_to_send; + + p_ccb->cont_offset += len_to_send; + + /* If anything left to send, continuation needed */ + if (p_ccb->cont_offset < p_ccb->list_len) + { + is_cont = TRUE; + + UINT8_TO_BE_STREAM (p_rsp, SDP_CONTINUATION_LEN); + UINT16_TO_BE_STREAM (p_rsp, p_ccb->cont_offset); + } + else + UINT8_TO_BE_STREAM (p_rsp, 0); + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + +#endif /* SDP_SERVER_ENABLED == TRUE */ diff --git a/stack/sdp/sdp_utils.c b/stack/sdp/sdp_utils.c new file mode 100644 index 0000000..cda0570 --- /dev/null +++ b/stack/sdp/sdp_utils.c @@ -0,0 +1,1040 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains SDP utility functions + * + ******************************************************************************/ + +#include +#include +#include +#include + +#include "gki.h" +#include "bt_types.h" + +#include "l2cdefs.h" +#include "hcidefs.h" +#include "hcimsgs.h" + +#include "sdp_api.h" +#include "sdpint.h" + +#include "btu.h" + + +static const UINT8 sdp_base_uuid[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}; + +/******************************************************************************* +** +** Function sdpu_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. +** +*******************************************************************************/ +tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid) +{ + UINT16 xx; + tCONN_CB *p_ccb; + + /* Look through each connection control block */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->connection_id == cid)) + return (p_ccb); + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_find_ccb_by_db +** +** Description This function searches the CCB table for an entry with the +** passed discovery db. +** +** Returns the CCB address, or NULL if not found. +** +*******************************************************************************/ +tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db) +{ +#if SDP_CLIENT_ENABLED == TRUE + UINT16 xx; + tCONN_CB *p_ccb; + + if (p_db) + { + /* Look through each connection control block */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if ((p_ccb->con_state != SDP_STATE_IDLE) && (p_ccb->p_db == p_db)) + return (p_ccb); + } + } +#endif + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_allocate_ccb +** +** Description This function allocates a new CCB. +** +** Returns CCB address, or NULL if none available. +** +*******************************************************************************/ +tCONN_CB *sdpu_allocate_ccb (void) +{ + UINT16 xx; + tCONN_CB *p_ccb; + + /* Look through each connection control block for a free one */ + for (xx = 0, p_ccb = sdp_cb.ccb; xx < SDP_MAX_CONNECTIONS; xx++, p_ccb++) + { + if (p_ccb->con_state == SDP_STATE_IDLE) + { + memset (p_ccb, 0, sizeof (tCONN_CB)); + + p_ccb->timer_entry.param = (UINT32) p_ccb; + + return (p_ccb); + } + } + + /* If here, no free CCB found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function sdpu_release_ccb +** +** Description This function releases a CCB. +** +** Returns void +** +*******************************************************************************/ +void sdpu_release_ccb (tCONN_CB *p_ccb) +{ + /* Ensure timer is stopped */ + btu_stop_timer (&p_ccb->timer_entry); + + /* Drop any response pointer we may be holding */ + p_ccb->con_state = SDP_STATE_IDLE; +#if SDP_CLIENT_ENABLED == TRUE + p_ccb->is_attr_search = FALSE; +#endif + + /* Free the response buffer */ + if (p_ccb->rsp_list) + { + SDP_TRACE_DEBUG0("releasing SDP rsp_list"); + + GKI_freebuf(p_ccb->rsp_list); + p_ccb->rsp_list = NULL; + } +} + + +/******************************************************************************* +** +** Function sdpu_build_attrib_seq +** +** Description This function builds an attribute sequence from the list of +** passed attributes. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs) +{ + UINT16 xx; + + /* First thing is the data element header. See if the length fits 1 byte */ + /* If no attributes, assume a 4-byte wildcard */ + if (!p_attr) + xx = 5; + else + xx = num_attrs * 3; + + if (xx > 255) + { + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_out, xx); + } + else + { + UINT8_TO_BE_STREAM (p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, xx); + } + + /* If there are no attributes specified, assume caller wants wildcard */ + if (!p_attr) + { + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT16_TO_BE_STREAM (p_out, 0); + UINT16_TO_BE_STREAM (p_out, 0xFFFF); + } + else + { + /* Loop through and put in all the attributes(s) */ + for (xx = 0; xx < num_attrs; xx++, p_attr++) + { + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, *p_attr); + } + } + + return (p_out); +} + + +/******************************************************************************* +** +** Function sdpu_build_attrib_entry +** +** Description This function builds an attribute entry from the passed +** attribute record. It is also passed the address of the output +** buffer. +** +** Returns Pointer to next byte in the output buffer. +** +*******************************************************************************/ +UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr) +{ + /* First, store the attribute ID. Goes as a UINT */ + UINT8_TO_BE_STREAM (p_out, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM (p_out, p_attr->id); + + /* the attribute is in the db record. + * assuming the attribute len is less than SDP_MAX_ATTR_LEN */ + switch(p_attr->type) + { + case TEXT_STR_DESC_TYPE: /* 4 */ + case DATA_ELE_SEQ_DESC_TYPE:/* 6 */ + case DATA_ELE_ALT_DESC_TYPE:/* 7 */ + case URL_DESC_TYPE: /* 8 */ +#if (SDP_MAX_ATTR_LEN > 0xFFFF) + if(p_attr->len > 0xFFFF) + { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_LONG); + UINT32_TO_BE_STREAM (p_out, p_attr->len); + } + else + +#endif /* 0xFFFF - 0xFF */ +#if (SDP_MAX_ATTR_LEN > 0xFF) + if(p_attr->len > 0xFF) + { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_WORD); + UINT16_TO_BE_STREAM (p_out, p_attr->len); + } + else + +#endif /* 0xFF and less*/ + { + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, p_attr->len); + } + + ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len); + + return (p_out); + } + + /* Now, store the attribute value */ + switch (p_attr->len) + { + case 1: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_ONE_BYTE); + break; + case 2: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_TWO_BYTES); + break; + case 4: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_FOUR_BYTES); + break; + case 8: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_EIGHT_BYTES); + break; + case 16: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_SIXTEEN_BYTES); + break; + default: + UINT8_TO_BE_STREAM (p_out, (p_attr->type << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM (p_out, p_attr->len); + break; + } + + ARRAY_TO_BE_STREAM (p_out, p_attr->value_ptr, (int)p_attr->len); + + return (p_out); +} + + +/******************************************************************************* +** +** Function sdpu_build_n_send_error +** +** Description This function builds and sends an error packet. +** +** Returns void +** +*******************************************************************************/ +void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text) +{ + UINT8 *p_rsp, *p_rsp_start, *p_rsp_param_len; + UINT16 rsp_param_len; + BT_HDR *p_buf; + + + SDP_TRACE_WARNING2 ("SDP - sdpu_build_n_send_error code: 0x%x CID: 0x%x", + error_code, p_ccb->connection_id); + + /* Get a buffer to use to build and send the packet to L2CAP */ + if ((p_buf = (BT_HDR *)GKI_getpoolbuf (SDP_POOL_ID)) == NULL) + { + SDP_TRACE_ERROR0 ("SDP - no buf for err msg"); + return; + } + p_buf->offset = L2CAP_MIN_OFFSET; + p_rsp = p_rsp_start = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_BE_STREAM (p_rsp, SDP_PDU_ERROR_RESPONSE); + UINT16_TO_BE_STREAM (p_rsp, trans_num); + + /* Skip the parameter length, we need to add it at the end */ + p_rsp_param_len = p_rsp; + p_rsp += 2; + + UINT16_TO_BE_STREAM (p_rsp, error_code); + + /* Unplugfest example traces do not have any error text */ + if (p_error_text) + ARRAY_TO_BE_STREAM (p_rsp, p_error_text, (int) strlen (p_error_text)); + + /* Go back and put the parameter length into the buffer */ + rsp_param_len = p_rsp - p_rsp_param_len - 2; + UINT16_TO_BE_STREAM (p_rsp_param_len, rsp_param_len); + + /* Set the length of the SDP data in the buffer */ + p_buf->len = p_rsp - p_rsp_start; + + + /* Send the buffer through L2CAP */ + L2CA_DataWrite (p_ccb->connection_id, p_buf); +} + + + +/******************************************************************************* +** +** Function sdpu_extract_uid_seq +** +** Description This function extracts a UUID sequence from the passed input +** buffer, and puts it into the passed output list. +** +** Returns Pointer to next byte in the input buffer after the sequence. +** +*******************************************************************************/ +UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq) +{ + UINT8 *p_seq_end; + UINT8 descr, type, size; + UINT32 seq_len, uuid_len; + + /* Assume none found */ + p_seq->num_uids = 0; + + /* A UID sequence is composed of a bunch of UIDs. */ + + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != DATA_ELE_SEQ_DESC_TYPE) + return (NULL); + + switch (size) + { + case SIZE_TWO_BYTES: + seq_len = 2; + break; + case SIZE_FOUR_BYTES: + seq_len = 4; + break; + case SIZE_SIXTEEN_BYTES: + seq_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (seq_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (seq_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (seq_len, p); + break; + default: + return (NULL); + } + + if (seq_len >= param_len) + return (NULL); + + p_seq_end = p + seq_len; + + /* Loop through, extracting the UIDs */ + for ( ; p < p_seq_end ; ) + { + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != UUID_DESC_TYPE) + return (NULL); + + switch (size) + { + case SIZE_TWO_BYTES: + uuid_len = 2; + break; + case SIZE_FOUR_BYTES: + uuid_len = 4; + break; + case SIZE_SIXTEEN_BYTES: + uuid_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (uuid_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (uuid_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (uuid_len, p); + break; + default: + return (NULL); + } + + /* If UUID length is valid, copy it across */ + if ((uuid_len == 2) || (uuid_len == 4) || (uuid_len == 16)) + { + p_seq->uuid_entry[p_seq->num_uids].len = (UINT16) uuid_len; + BE_STREAM_TO_ARRAY (p, p_seq->uuid_entry[p_seq->num_uids].value, (int)uuid_len); + p_seq->num_uids++; + } + else + return (NULL); + + /* We can only do so many */ + if (p_seq->num_uids >= MAX_UUIDS_PER_SEQ) + return (NULL); + } + + if (p != p_seq_end) + return (NULL); + + return (p); +} + + + +/******************************************************************************* +** +** Function sdpu_extract_attr_seq +** +** Description This function extracts an attribute sequence from the passed +** input buffer, and puts it into the passed output list. +** +** Returns Pointer to next byte in the input buffer after the sequence. +** +*******************************************************************************/ +UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq) +{ + UINT8 *p_end_list; + UINT8 descr, type, size; + UINT32 list_len, attr_len; + + /* Assume none found */ + p_seq->num_attr = 0; + + /* Get attribute sequence info */ + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != DATA_ELE_SEQ_DESC_TYPE) + return (p); + + switch (size) + { + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (list_len, p); + break; + + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (list_len, p); + break; + + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (list_len, p); + break; + + default: + return (p); + } + + if (list_len > param_len) + return (p); + + p_end_list = p + list_len; + + /* Loop through, extracting the attribute IDs */ + for ( ; p < p_end_list ; ) + { + BE_STREAM_TO_UINT8 (descr, p); + type = descr >> 3; + size = descr & 7; + + if (type != UINT_DESC_TYPE) + return (p); + + switch (size) + { + case SIZE_TWO_BYTES: + attr_len = 2; + break; + case SIZE_FOUR_BYTES: + attr_len = 4; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (attr_len, p); + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (attr_len, p); + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (attr_len, p); + break; + default: + return (NULL); + break; + } + + /* Attribute length must be 2-bytes or 4-bytes for a paired entry. */ + if (attr_len == 2) + { + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p); + p_seq->attr_entry[p_seq->num_attr].end = p_seq->attr_entry[p_seq->num_attr].start; + } + else if (attr_len == 4) + { + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].start, p); + BE_STREAM_TO_UINT16 (p_seq->attr_entry[p_seq->num_attr].end, p); + } + else + return (NULL); + + /* We can only do so many */ + if (++p_seq->num_attr >= MAX_ATTR_PER_SEQ) + return (NULL); + } + + return (p); +} + + +/******************************************************************************* +** +** Function sdpu_get_len_from_type +** +** Description This function gets the length +** +** Returns void +** +*******************************************************************************/ +UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len) +{ + UINT8 u8; + UINT16 u16; + UINT32 u32; + + switch (type & 7) + { + case SIZE_ONE_BYTE: + *p_len = 1; + break; + case SIZE_TWO_BYTES: + *p_len = 2; + break; + case SIZE_FOUR_BYTES: + *p_len = 4; + break; + case SIZE_EIGHT_BYTES: + *p_len = 8; + break; + case SIZE_SIXTEEN_BYTES: + *p_len = 16; + break; + case SIZE_IN_NEXT_BYTE: + BE_STREAM_TO_UINT8 (u8, p); + *p_len = u8; + break; + case SIZE_IN_NEXT_WORD: + BE_STREAM_TO_UINT16 (u16, p); + *p_len = u16; + break; + case SIZE_IN_NEXT_LONG: + BE_STREAM_TO_UINT32 (u32, p); + *p_len = (UINT16) u32; + break; + } + + return (p); +} + + +/******************************************************************************* +** +** Function sdpu_is_base_uuid +** +** Description This function checks a 128-bit UUID with the base to see if +** it matches. Only the last 12 bytes are compared. +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid) +{ + UINT16 xx; + + for (xx = 4; xx < MAX_UUID_SIZE; xx++) + if (p_uuid[xx] != sdp_base_uuid[xx]) + return (FALSE); + + /* If here, matched */ + return (TRUE); +} + + +/******************************************************************************* +** +** Function sdpu_compare_uuid_arrays +** +** Description This function compares 2 BE UUIDs. If needed, they are expanded +** to 128-bit UUIDs, then compared. +** +** NOTE it is assumed that the arrays are in Big Endian format +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2) +{ + UINT8 nu1[MAX_UUID_SIZE]; + UINT8 nu2[MAX_UUID_SIZE]; + + /* If lengths match, do a straight compare */ + if (len1 == len2) + { + if (len1 == 2) + return ((p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1])); + if (len1 == 4) + return ( (p_uuid1[0] == p_uuid2[0]) && (p_uuid1[1] == p_uuid2[1]) + && (p_uuid1[2] == p_uuid2[2]) && (p_uuid1[3] == p_uuid2[3]) ); + else + return (memcmp (p_uuid1, p_uuid2, (size_t)len1) == 0); + } + else if (len1 > len2) + { + /* If the len1 was 4-byte, (so len2 is 2-byte), compare on the fly */ + if (len1 == 4) + { + return ( (p_uuid1[0] == 0) && (p_uuid1[1] == 0) + && (p_uuid1[2] == p_uuid2[0]) && (p_uuid1[3] == p_uuid2[1]) ); + } + else + { + /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */ + memcpy (nu1, p_uuid1, MAX_UUID_SIZE); + memcpy (nu2, sdp_base_uuid, MAX_UUID_SIZE); + + if (len2 == 4) + memcpy (nu2, p_uuid2, len2); + else + memcpy (nu2 + 2, p_uuid2, len2); + + return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0); + } + } + else + { + /* len2 is greater than len1 */ + /* If the len2 was 4-byte, (so len1 is 2-byte), compare on the fly */ + if (len2 == 4) + { + return ( (p_uuid2[0] == 0) && (p_uuid2[1] == 0) + && (p_uuid2[2] == p_uuid1[0]) && (p_uuid2[3] == p_uuid1[1]) ); + } + else + { + /* Normalize UUIDs to 16-byte form, then compare. Len1 must be 16 */ + memcpy (nu2, p_uuid2, MAX_UUID_SIZE); + memcpy (nu1, sdp_base_uuid, MAX_UUID_SIZE); + + if (len1 == 4) + memcpy (nu1, p_uuid1, (size_t)len1); + else + memcpy (nu1 + 2, p_uuid1, (size_t)len1); + + return (memcmp (nu1, nu2, MAX_UUID_SIZE) == 0); + } + } +} + + +/******************************************************************************* +** +** Function sdpu_compare_bt_uuids +** +** Description This function compares 2 BT UUID structures. +** +** NOTE it is assumed that BT UUID structures are compressed to the +** smallest possible UUIDs (by removing the base SDP UUID) +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2) +{ + /* Lengths must match for BT UUIDs to match */ + if (p_uuid1->len == p_uuid2->len) + { + if (p_uuid1->len == 2) + return (p_uuid1->uu.uuid16 == p_uuid2->uu.uuid16); + else if (p_uuid1->len == 4) + return (p_uuid1->uu.uuid32 == p_uuid2->uu.uuid32); + else if (!memcmp (p_uuid1->uu.uuid128, p_uuid2->uu.uuid128, 16)) + return (TRUE); + } + + return (FALSE); +} + + +/******************************************************************************* +** +** Function sdpu_compare_uuid_with_attr +** +** Description This function compares a BT UUID structure with the UUID in an +** SDP attribute record. If needed, they are expanded to 128-bit +** UUIDs, then compared. +** +** NOTE - it is assumed that BT UUID structures are compressed to the +** smallest possible UUIDs (by removing the base SDP UUID). +** - it is also assumed that the discovery atribute is compressed +** to the smallest possible +** +** Returns TRUE if matched, else FALSE +** +*******************************************************************************/ +BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr) +{ + UINT16 attr_len = SDP_DISC_ATTR_LEN (p_attr->attr_len_type); + + /* Since both UUIDs are compressed, lengths must match */ + if (p_btuuid->len != attr_len) + return (FALSE); + + if (p_btuuid->len == 2) + return (BOOLEAN)(p_btuuid->uu.uuid16 == p_attr->attr_value.v.u16); + else if (p_btuuid->len == 4) + return (BOOLEAN)(p_btuuid->uu.uuid32 == p_attr->attr_value.v.u32); + /* coverity[overrun-buffer-arg] */ + /* + Event overrun-buffer-arg: Overrun of static array "&p_attr->attr_value.v.array" of size 4 bytes by passing it to a function which indexes it with argument "16U" at byte position 15 + FALSE-POSITIVE error from Coverity test tool. Please do NOT remove following comment. + False-positive: SDP uses scratch buffer to hold the attribute value. + The actual size of tSDP_DISC_ATVAL does not matter. + If the array size in tSDP_DISC_ATVAL is increase, we would increase the system RAM usage unnecessarily + */ + else if (!memcmp (p_btuuid->uu.uuid128, p_attr->attr_value.v.array, MAX_UUID_SIZE)) + return (TRUE); + + return (FALSE); +} + +/******************************************************************************* +** +** Function sdpu_sort_attr_list +** +** Description sorts a list of attributes in numeric order from lowest to +** highest to conform to SDP specification +** +** Returns void +** +*******************************************************************************/ +void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db ) +{ + UINT16 i; + UINT16 x; + + /* Done if no attributes to sort */ + if (num_attr <= 1) + { + return; + } + else if (num_attr > SDP_MAX_ATTR_FILTERS) + { + num_attr = SDP_MAX_ATTR_FILTERS; + } + + num_attr--; /* for the for-loop */ + for( i = 0; i < num_attr; ) + { + if( p_db->attr_filters[i] > p_db->attr_filters[i+1] ) + { + /* swap the attribute IDs and start from the beginning */ + x = p_db->attr_filters[i]; + p_db->attr_filters[i] = p_db->attr_filters[i+1]; + p_db->attr_filters[i+1] = x; + + i = 0; + } + else + i++; + } +} + + +/******************************************************************************* +** +** Function sdpu_get_list_len +** +** Description gets the total list length in the sdp database for a given +** uid sequence and attr sequence +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_list_len(tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq) +{ + tSDP_RECORD *p_rec; + UINT16 len = 0; + UINT16 len1; + + for (p_rec = sdp_db_service_search (NULL, uid_seq); p_rec; p_rec = sdp_db_service_search (p_rec, uid_seq)) + { + len += 3; + + len1 = sdpu_get_attrib_seq_len(p_rec, attr_seq ); + + if (len1 != 0) + len += len1; + else + len -= 3; + } + return len; +} + +/******************************************************************************* +** +** Function sdpu_get_attrib_seq_len +** +** Description gets the length of the specific attributes in a given +** sdp record +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq) +{ + tSDP_ATTRIBUTE *p_attr; + UINT16 len1 = 0; + UINT16 xx; + BOOLEAN is_range = FALSE; + UINT16 start_id, end_id; + + for (xx = 0; xx < attr_seq->num_attr; xx++) + { + if (is_range == FALSE) + { + start_id = attr_seq->attr_entry[xx].start; + end_id = attr_seq->attr_entry[xx].end; + } + p_attr = sdp_db_find_attr_in_rec (p_rec, + start_id, + end_id); + if (p_attr) + { + len1 += sdpu_get_attrib_entry_len (p_attr); + + /* If doing a range, stick with this one till no more attributes found */ + if (start_id != end_id) + { + /* Update for next time through */ + start_id = p_attr->id + 1; + xx--; + is_range = TRUE; + } + else + is_range = FALSE; + } + else + is_range = FALSE; + } + return len1; +} + +/******************************************************************************* +** +** Function sdpu_get_attrib_entry_len +** +** Description gets the length of a specific attribute +** +** Returns void +** +*******************************************************************************/ +UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr) +{ + UINT16 len = 3; + + /* the attribute is in the db record. + * assuming the attribute len is less than SDP_MAX_ATTR_LEN */ + switch(p_attr->type) + { + case TEXT_STR_DESC_TYPE: /* 4 */ + case DATA_ELE_SEQ_DESC_TYPE:/* 6 */ + case DATA_ELE_ALT_DESC_TYPE:/* 7 */ + case URL_DESC_TYPE: /* 8 */ +#if (SDP_MAX_ATTR_LEN > 0xFFFF) + if(p_attr->len > 0xFFFF) + { + len += 5; + } + else + +#endif/* 0xFFFF - 0xFF */ +#if (SDP_MAX_ATTR_LEN > 0xFF) + if(p_attr->len > 0xFF) + { + len += 3; + } + else + +#endif /* 0xFF and less*/ + { + len += 2; + } + len += p_attr->len; + return len; + } + + /* Now, the attribute value */ + switch (p_attr->len) + { + case 1: + case 2: + case 4: + case 8: + case 16: + len += 1; + break; + default: + len += 2; + break; + } + + len += p_attr->len; + return len; +} + + +/******************************************************************************* +** +** Function sdpu_build_partial_attrib_entry +** +** Description This function fills a buffer with partial attribute. It is +** assumed that the maximum size of any attribute is 256 bytes. +** +** p_out: output buffer +** p_attr: attribute to be copied partially into p_out +** rem_len: num bytes to copy into p_out +** offset: current start offset within the attr that needs to be copied +** +** Returns Pointer to next byte in the output buffer. +** offset is also updated +** +*******************************************************************************/ +UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset) +{ + UINT8 tmp_attr[MAX_ATTR_LEN]; + UINT8 *p_tmp_attr = &tmp_attr[0]; + size_t len_to_copy; + UINT16 attr_len; + + sdpu_build_attrib_entry(p_tmp_attr, p_attr); + attr_len = sdpu_get_attrib_entry_len(p_attr); + + len_to_copy = ((attr_len - *offset) < len) ? (attr_len - *offset): len; + + memcpy(p_out, &tmp_attr[*offset], len_to_copy); + + p_out = &p_out[len_to_copy]; + *offset += len_to_copy; + + return p_out; +} + +/******************************************************************************* +** +** Function sdpu_uuid16_to_uuid128 +** +** Description This function converts UUID-16 to UUID-128 by including the base UUID +** +** uuid16: 2-byte UUID +** p_uuid128: Expanded 128-bit UUID +** +** Returns None +** +*******************************************************************************/ +void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8* p_uuid128) +{ + UINT16 uuid16_bo; + memset(p_uuid128, 0, 16); + + memcpy(p_uuid128, sdp_base_uuid, MAX_UUID_SIZE); + uuid16_bo = ntohs(uuid16); + memcpy(p_uuid128+ 2, &uuid16_bo, sizeof(uint16_t)); +} diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h new file mode 100644 index 0000000..b300664 --- /dev/null +++ b/stack/sdp/sdpint.h @@ -0,0 +1,329 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used SDP definitions + * + ******************************************************************************/ + +#ifndef SDP_INT_H +#define SDP_INT_H + +#include "bt_target.h" +#include "sdp_api.h" +#include "l2c_api.h" + + +/* Continuation length - we use a 2-byte offset */ +#define SDP_CONTINUATION_LEN 2 +#define SDP_MAX_CONTINUATION_LEN 16 /* As per the spec */ + +/* Timeout definitions. */ +#define SDP_INACT_TIMEOUT 30 /* Inactivity timeout */ + + +/* Define the Out-Flow default values. */ +#define SDP_OFLOW_QOS_FLAG 0 +#define SDP_OFLOW_SERV_TYPE 0 +#define SDP_OFLOW_TOKEN_RATE 0 +#define SDP_OFLOW_TOKEN_BUCKET_SIZE 0 +#define SDP_OFLOW_PEAK_BANDWIDTH 0 +#define SDP_OFLOW_LATENCY 0 +#define SDP_OFLOW_DELAY_VARIATION 0 + +/* Define the In-Flow default values. */ +#define SDP_IFLOW_QOS_FLAG 0 +#define SDP_IFLOW_SERV_TYPE 0 +#define SDP_IFLOW_TOKEN_RATE 0 +#define SDP_IFLOW_TOKEN_BUCKET_SIZE 0 +#define SDP_IFLOW_PEAK_BANDWIDTH 0 +#define SDP_IFLOW_LATENCY 0 +#define SDP_IFLOW_DELAY_VARIATION 0 + +#define SDP_LINK_TO 0 + +/* Define the type of device notification. */ +/* (Inquiry Scan and Page Scan) */ +#define SDP_DEVICE_NOTI_LEN sizeof (BT_HDR) + \ + HCIC_PREAMBLE_SIZE + \ + HCIC_PARAM_SIZE_WRITE_PARAM1 + +#define SDP_DEVICE_NOTI_FLAG 0x03 + +/* Define the Protocol Data Unit (PDU) types. +*/ +#define SDP_PDU_ERROR_RESPONSE 0x01 +#define SDP_PDU_SERVICE_SEARCH_REQ 0x02 +#define SDP_PDU_SERVICE_SEARCH_RSP 0x03 +#define SDP_PDU_SERVICE_ATTR_REQ 0x04 +#define SDP_PDU_SERVICE_ATTR_RSP 0x05 +#define SDP_PDU_SERVICE_SEARCH_ATTR_REQ 0x06 +#define SDP_PDU_SERVICE_SEARCH_ATTR_RSP 0x07 + +/* Max UUIDs and attributes we support per sequence */ +#define MAX_UUIDS_PER_SEQ 16 +#define MAX_ATTR_PER_SEQ 16 + +/* Max length we support for any attribute */ +// btla-specific ++ +#ifdef SDP_MAX_ATTR_LEN +#define MAX_ATTR_LEN SDP_MAX_ATTR_LEN +#else +#define MAX_ATTR_LEN 256 +#endif +// btla-specific -- + +/* Internal UUID sequence representation */ +typedef struct +{ + UINT16 len; + UINT8 value[MAX_UUID_SIZE]; +} tUID_ENT; + +typedef struct +{ + UINT16 num_uids; + tUID_ENT uuid_entry[MAX_UUIDS_PER_SEQ]; +} tSDP_UUID_SEQ; + + +/* Internal attribute sequence definitions */ +typedef struct +{ + UINT16 start; + UINT16 end; +} tATT_ENT; + +typedef struct +{ + UINT16 num_attr; + tATT_ENT attr_entry[MAX_ATTR_PER_SEQ]; +} tSDP_ATTR_SEQ; + + +/* Define the attribute element of the SDP database record */ +typedef struct +{ + UINT32 len; /* Number of bytes in the entry */ + UINT8 *value_ptr; /* Points to attr_pad */ + UINT16 id; + UINT8 type; +} tSDP_ATTRIBUTE; + +/* An SDP record consists of a handle, and 1 or more attributes */ +typedef struct +{ + UINT32 record_handle; + UINT32 free_pad_ptr; + UINT16 num_attributes; + tSDP_ATTRIBUTE attribute[SDP_MAX_REC_ATTR]; + UINT8 attr_pad[SDP_MAX_PAD_LEN]; +} tSDP_RECORD; + + +/* Define the SDP database */ +typedef struct +{ + UINT32 di_primary_handle; /* Device ID Primary record or NULL if nonexistent */ + BOOLEAN brcm_di_registered; + UINT16 num_records; + tSDP_RECORD record[SDP_MAX_RECORDS]; +} tSDP_DB; + +enum +{ + SDP_IS_SEARCH, + SDP_IS_ATTR_SEARCH, + SDP_IS_PASS_THRU /* only when SDP_FOR_JV_INCLUDED == TRUE */ +}; + +#if SDP_SERVER_ENABLED == TRUE +/* Continuation information for the SDP server response */ +typedef struct +{ + UINT16 next_attr_index; /* attr index for next continuation response */ + UINT16 next_attr_start_id; /* attr id to start with for the attr index in next cont. response */ + tSDP_RECORD *prev_sdp_rec; /* last sdp record that was completely sent in the response */ + BOOLEAN last_attr_seq_desc_sent; /* whether attr seq length has been sent previously */ + UINT16 attr_offset; /* offset within the attr to keep trak of partial attributes in the responses */ +} tSDP_CONT_INFO; +#endif /* SDP_SERVER_ENABLED == TRUE */ + +/* Define the SDP Connection Control Block */ +typedef struct +{ +#define SDP_STATE_IDLE 0 +#define SDP_STATE_CONN_SETUP 1 +#define SDP_STATE_CFG_SETUP 2 +#define SDP_STATE_CONNECTED 3 + UINT8 con_state; + +#define SDP_FLAGS_IS_ORIG 0x01 +#define SDP_FLAGS_HIS_CFG_DONE 0x02 +#define SDP_FLAGS_MY_CFG_DONE 0x04 + UINT8 con_flags; + + BD_ADDR device_address; + TIMER_LIST_ENT timer_entry; + UINT16 rem_mtu_size; + UINT16 connection_id; + UINT16 list_len; /* length of the response in the GKI buffer */ + UINT8 *rsp_list; /* pointer to GKI buffer holding response */ + +#if SDP_CLIENT_ENABLED == TRUE + tSDP_DISCOVERY_DB *p_db; /* Database to save info into */ + tSDP_DISC_CMPL_CB *p_cb; /* Callback for discovery done */ + tSDP_DISC_CMPL_CB2 *p_cb2; /* Callback for discovery done piggy back with the user data */ + void *user_data; /* piggy back user data */ + UINT32 handles[SDP_MAX_DISC_SERVER_RECS]; /* Discovered server record handles */ + UINT16 num_handles; /* Number of server handles */ + UINT16 cur_handle; /* Current handle being processed */ + UINT16 transaction_id; + UINT16 disconnect_reason; /* Disconnect reason */ +#if (defined(SDP_BROWSE_PLUS) && SDP_BROWSE_PLUS == TRUE) + UINT16 cur_uuid_idx; +#endif + +#define SDP_DISC_WAIT_CONN 0 +#define SDP_DISC_WAIT_HANDLES 1 +#define SDP_DISC_WAIT_ATTR 2 +#define SDP_DISC_WAIT_SEARCH_ATTR 3 +#define SDP_DISC_WAIT_PASS_THRU 4 /* only when SDP_FOR_JV_INCLUDED == TRUE */ +#define SDP_DISC_WAIT_CANCEL 5 + + UINT8 disc_state; + UINT8 is_attr_search; +#endif /* SDP_CLIENT_ENABLED == TRUE */ + +#if SDP_SERVER_ENABLED == TRUE + UINT16 cont_offset; /* Continuation state data in the server response */ + tSDP_CONT_INFO cont_info; /* structure to hold continuation information for the server response */ +#endif /* SDP_SERVER_ENABLED == TRUE */ + +} tCONN_CB; + + +/* The main SDP control block */ +typedef struct +{ + tL2CAP_CFG_INFO l2cap_my_cfg; /* My L2CAP config */ + tCONN_CB ccb[SDP_MAX_CONNECTIONS]; +#if SDP_SERVER_ENABLED == TRUE + tSDP_DB server_db; +#endif + tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */ + UINT16 max_attr_list_size; /* Max attribute list size to use */ + UINT16 max_recs_per_search; /* Max records we want per seaarch */ + UINT8 trace_level; +} tSDP_CB; + +#ifdef __cplusplus +extern "C" { +#endif +/* Global SDP data */ +#if SDP_DYNAMIC_MEMORY == FALSE +SDP_API extern tSDP_CB sdp_cb; +#else +SDP_API extern tSDP_CB *sdp_cb_ptr; +#define sdp_cb (*sdp_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +/* Functions provided by sdp_main.c */ +SDP_API extern void sdp_init (void); +extern void sdp_disconnect (tCONN_CB*p_ccb, UINT16 reason); + +#if (defined(SDP_DEBUG) && SDP_DEBUG == TRUE) +SDP_API extern UINT16 sdp_set_max_attr_list_size (UINT16 max_size); +#endif + +/* Functions provided by sdp_conn.c +*/ +extern void sdp_conn_rcv_l2e_conn_ind (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_cfm (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_disc (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_config_ind (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_config_cfm (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_connected (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_conn_failed (BT_HDR *p_msg); +extern void sdp_conn_rcv_l2e_data (BT_HDR *p_msg); +extern void sdp_conn_timeout (tCONN_CB *p_ccb); + +extern tCONN_CB *sdp_conn_originate (UINT8 *p_bd_addr); + +/* Functions provided by sdp_utils.c +*/ +extern tCONN_CB *sdpu_find_ccb_by_cid (UINT16 cid); +extern tCONN_CB *sdpu_find_ccb_by_db (tSDP_DISCOVERY_DB *p_db); +extern tCONN_CB *sdpu_allocate_ccb (void); +extern void sdpu_release_ccb (tCONN_CB *p_ccb); + +extern UINT8 *sdpu_build_attrib_seq (UINT8 *p_out, UINT16 *p_attr, UINT16 num_attrs); +extern UINT8 *sdpu_build_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr); +extern void sdpu_build_n_send_error (tCONN_CB *p_ccb, UINT16 trans_num, UINT16 error_code, char *p_error_text); + +extern UINT8 *sdpu_extract_attr_seq (UINT8 *p, UINT16 param_len, tSDP_ATTR_SEQ *p_seq); +extern UINT8 *sdpu_extract_uid_seq (UINT8 *p, UINT16 param_len, tSDP_UUID_SEQ *p_seq); + +SDP_API extern UINT8 *sdpu_get_len_from_type (UINT8 *p, UINT8 type, UINT32 *p_len); +extern BOOLEAN sdpu_is_base_uuid (UINT8 *p_uuid); +extern BOOLEAN sdpu_compare_uuid_arrays (UINT8 *p_uuid1, UINT32 len1, UINT8 *p_uuid2, UINT16 len2); +SDP_API extern BOOLEAN sdpu_compare_bt_uuids (tBT_UUID *p_uuid1, tBT_UUID *p_uuid2); +extern BOOLEAN sdpu_compare_uuid_with_attr (tBT_UUID *p_btuuid, tSDP_DISC_ATTR *p_attr); + +extern void sdpu_sort_attr_list( UINT16 num_attr, tSDP_DISCOVERY_DB *p_db ); +extern UINT16 sdpu_get_list_len( tSDP_UUID_SEQ *uid_seq, tSDP_ATTR_SEQ *attr_seq ); +extern UINT16 sdpu_get_attrib_seq_len(tSDP_RECORD *p_rec, tSDP_ATTR_SEQ *attr_seq); +extern UINT16 sdpu_get_attrib_entry_len(tSDP_ATTRIBUTE *p_attr); +extern UINT8 *sdpu_build_partial_attrib_entry (UINT8 *p_out, tSDP_ATTRIBUTE *p_attr, UINT16 len, UINT16 *offset); +extern void sdpu_uuid16_to_uuid128(UINT16 uuid16, UINT8* p_uuid128); + +/* Functions provided by sdp_db.c +*/ +extern tSDP_RECORD *sdp_db_service_search (tSDP_RECORD *p_rec, tSDP_UUID_SEQ *p_seq); +extern tSDP_RECORD *sdp_db_find_record (UINT32 handle); +extern tSDP_ATTRIBUTE *sdp_db_find_attr_in_rec (tSDP_RECORD *p_rec, UINT16 start_attr, UINT16 end_attr); + + +/* Functions provided by sdp_server.c +*/ +#if SDP_SERVER_ENABLED == TRUE +extern void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg); +#else +#define sdp_server_handle_client_req(p_ccb, p_msg) +#endif + +/* Functions provided by sdp_discovery.c +*/ +#if SDP_CLIENT_ENABLED == TRUE +extern void sdp_disc_connected (tCONN_CB *p_ccb); +extern void sdp_disc_server_rsp (tCONN_CB *p_ccb, BT_HDR *p_msg); +#else +#define sdp_disc_connected(p_ccb) +#define sdp_disc_server_rsp(p_ccb, p_msg) +#endif + + + +#endif + diff --git a/stack/smp/aes.c b/stack/smp/aes.c new file mode 100644 index 0000000..1028d5b --- /dev/null +++ b/stack/smp/aes.c @@ -0,0 +1,926 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state (there are options to use 32-bit types if available). + + The combination of mix columns and byte substitution used here is based on + that developed by Karl Malbrain. His contribution is acknowledged. + */ + +/* define if you have a fast memcpy function on your system */ +#if 1 +# define HAVE_MEMCPY +# include +#if 0 +# if defined( _MSC_VER ) +# include +# pragma intrinsic( memcpy ) +# endif +#endif +#endif + +#include + +/* define if you have fast 32-bit types on your system */ +#if 1 +# define HAVE_UINT_32T +#endif + +/* define if you don't want any tables */ +#if 1 +# define USE_TABLES +#endif + +/* On Intel Core 2 duo VERSION_1 is faster */ + +/* alternative versions (test for performance on your system) */ +#if 1 +# define VERSION_1 +#endif + +#include "aes.h" + +#if defined( HAVE_UINT_32T ) + typedef unsigned long uint_32t; +#endif + +/* functions for finite field multiplication in the AES Galois field */ + +#define WPOLY 0x011b +#define BPOLY 0x1b +#define DPOLY 0x008d + +#define f1(x) (x) +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) \ + ^ (((x >> 5) & 4) * WPOLY)) +#define d2(x) (((x) >> 1) ^ ((x) & 1 ? DPOLY : 0)) + +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#if defined( USE_TABLES ) + +#define sb_data(w) { /* S Box data values */ \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) { /* inverse S Box data values */ \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) { /* basic data for forming finite field tables */ \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +static const uint_8t sbox[256] = sb_data(f1); +static const uint_8t isbox[256] = isb_data(f1); + +static const uint_8t gfm2_sbox[256] = sb_data(f2); +static const uint_8t gfm3_sbox[256] = sb_data(f3); + +static const uint_8t gfmul_9[256] = mm_data(f9); +static const uint_8t gfmul_b[256] = mm_data(fb); +static const uint_8t gfmul_d[256] = mm_data(fd); +static const uint_8t gfmul_e[256] = mm_data(fe); + +#define s_box(x) sbox[(x)] +#define is_box(x) isbox[(x)] +#define gfm2_sb(x) gfm2_sbox[(x)] +#define gfm3_sb(x) gfm3_sbox[(x)] +#define gfm_9(x) gfmul_9[(x)] +#define gfm_b(x) gfmul_b[(x)] +#define gfm_d(x) gfmul_d[(x)] +#define gfm_e(x) gfmul_e[(x)] + +#else + +/* this is the high bit of x right shifted by 1 */ +/* position. Since the starting polynomial has */ +/* 9 bits (0x11b), this right shift keeps the */ +/* values of all top bits within a byte */ + +static uint_8t hibit(const uint_8t x) +{ uint_8t r = (uint_8t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint_8t gf_inv(const uint_8t x) +{ uint_8t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= (v1 * n2); /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +/* The forward and inverse affine transformations used in the S-box */ +uint_8t fwd_affine(const uint_8t x) +{ +#if defined( HAVE_UINT_32T ) + uint_32t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x63 ^ x ^ (x << 1) ^ (x << 2) ^ (x << 3) ^ (x << 4) + ^ (x >> 7) ^ (x >> 6) ^ (x >> 5) ^ (x >> 4); +#endif +} + +uint_8t inv_affine(const uint_8t x) +{ +#if defined( HAVE_UINT_32T ) + uint_32t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +#else + return 0x05 ^ (x << 1) ^ (x << 3) ^ (x << 6) + ^ (x >> 7) ^ (x >> 5) ^ (x >> 2); +#endif +} + +#define s_box(x) fwd_affine(gf_inv(x)) +#define is_box(x) gf_inv(inv_affine(x)) +#define gfm2_sb(x) f2(s_box(x)) +#define gfm3_sb(x) f3(s_box(x)) +#define gfm_9(x) f9(x) +#define gfm_b(x) fb(x) +#define gfm_d(x) fd(x) +#define gfm_e(x) fe(x) + +#endif + +#if defined( HAVE_MEMCPY ) +# define block_copy_nn(d, s, l) memcpy(d, s, l) +# define block_copy(d, s) memcpy(d, s, N_BLOCK) +#else +# define block_copy_nn(d, s, l) copy_block_nn(d, s, l) +# define block_copy(d, s) copy_block(d, s) +#endif + +#if !defined( HAVE_MEMCPY ) +static void copy_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t*)d)[ 0] = ((uint_32t*)s)[ 0]; + ((uint_32t*)d)[ 1] = ((uint_32t*)s)[ 1]; + ((uint_32t*)d)[ 2] = ((uint_32t*)s)[ 2]; + ((uint_32t*)d)[ 3] = ((uint_32t*)s)[ 3]; +#else + ((uint_8t*)d)[ 0] = ((uint_8t*)s)[ 0]; + ((uint_8t*)d)[ 1] = ((uint_8t*)s)[ 1]; + ((uint_8t*)d)[ 2] = ((uint_8t*)s)[ 2]; + ((uint_8t*)d)[ 3] = ((uint_8t*)s)[ 3]; + ((uint_8t*)d)[ 4] = ((uint_8t*)s)[ 4]; + ((uint_8t*)d)[ 5] = ((uint_8t*)s)[ 5]; + ((uint_8t*)d)[ 6] = ((uint_8t*)s)[ 6]; + ((uint_8t*)d)[ 7] = ((uint_8t*)s)[ 7]; + ((uint_8t*)d)[ 8] = ((uint_8t*)s)[ 8]; + ((uint_8t*)d)[ 9] = ((uint_8t*)s)[ 9]; + ((uint_8t*)d)[10] = ((uint_8t*)s)[10]; + ((uint_8t*)d)[11] = ((uint_8t*)s)[11]; + ((uint_8t*)d)[12] = ((uint_8t*)s)[12]; + ((uint_8t*)d)[13] = ((uint_8t*)s)[13]; + ((uint_8t*)d)[14] = ((uint_8t*)s)[14]; + ((uint_8t*)d)[15] = ((uint_8t*)s)[15]; +#endif +} + +static void copy_block_nn( void * d, const void *s, uint_8t nn ) +{ + while( nn-- ) + *((uint_8t*)d)++ = *((uint_8t*)s)++; +} +#endif + +static void xor_block( void *d, const void *s ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t*)d)[ 0] ^= ((uint_32t*)s)[ 0]; + ((uint_32t*)d)[ 1] ^= ((uint_32t*)s)[ 1]; + ((uint_32t*)d)[ 2] ^= ((uint_32t*)s)[ 2]; + ((uint_32t*)d)[ 3] ^= ((uint_32t*)s)[ 3]; +#else + ((uint_8t*)d)[ 0] ^= ((uint_8t*)s)[ 0]; + ((uint_8t*)d)[ 1] ^= ((uint_8t*)s)[ 1]; + ((uint_8t*)d)[ 2] ^= ((uint_8t*)s)[ 2]; + ((uint_8t*)d)[ 3] ^= ((uint_8t*)s)[ 3]; + ((uint_8t*)d)[ 4] ^= ((uint_8t*)s)[ 4]; + ((uint_8t*)d)[ 5] ^= ((uint_8t*)s)[ 5]; + ((uint_8t*)d)[ 6] ^= ((uint_8t*)s)[ 6]; + ((uint_8t*)d)[ 7] ^= ((uint_8t*)s)[ 7]; + ((uint_8t*)d)[ 8] ^= ((uint_8t*)s)[ 8]; + ((uint_8t*)d)[ 9] ^= ((uint_8t*)s)[ 9]; + ((uint_8t*)d)[10] ^= ((uint_8t*)s)[10]; + ((uint_8t*)d)[11] ^= ((uint_8t*)s)[11]; + ((uint_8t*)d)[12] ^= ((uint_8t*)s)[12]; + ((uint_8t*)d)[13] ^= ((uint_8t*)s)[13]; + ((uint_8t*)d)[14] ^= ((uint_8t*)s)[14]; + ((uint_8t*)d)[15] ^= ((uint_8t*)s)[15]; +#endif +} + +static void copy_and_key( void *d, const void *s, const void *k ) +{ +#if defined( HAVE_UINT_32T ) + ((uint_32t*)d)[ 0] = ((uint_32t*)s)[ 0] ^ ((uint_32t*)k)[ 0]; + ((uint_32t*)d)[ 1] = ((uint_32t*)s)[ 1] ^ ((uint_32t*)k)[ 1]; + ((uint_32t*)d)[ 2] = ((uint_32t*)s)[ 2] ^ ((uint_32t*)k)[ 2]; + ((uint_32t*)d)[ 3] = ((uint_32t*)s)[ 3] ^ ((uint_32t*)k)[ 3]; +#elif 1 + ((uint_8t*)d)[ 0] = ((uint_8t*)s)[ 0] ^ ((uint_8t*)k)[ 0]; + ((uint_8t*)d)[ 1] = ((uint_8t*)s)[ 1] ^ ((uint_8t*)k)[ 1]; + ((uint_8t*)d)[ 2] = ((uint_8t*)s)[ 2] ^ ((uint_8t*)k)[ 2]; + ((uint_8t*)d)[ 3] = ((uint_8t*)s)[ 3] ^ ((uint_8t*)k)[ 3]; + ((uint_8t*)d)[ 4] = ((uint_8t*)s)[ 4] ^ ((uint_8t*)k)[ 4]; + ((uint_8t*)d)[ 5] = ((uint_8t*)s)[ 5] ^ ((uint_8t*)k)[ 5]; + ((uint_8t*)d)[ 6] = ((uint_8t*)s)[ 6] ^ ((uint_8t*)k)[ 6]; + ((uint_8t*)d)[ 7] = ((uint_8t*)s)[ 7] ^ ((uint_8t*)k)[ 7]; + ((uint_8t*)d)[ 8] = ((uint_8t*)s)[ 8] ^ ((uint_8t*)k)[ 8]; + ((uint_8t*)d)[ 9] = ((uint_8t*)s)[ 9] ^ ((uint_8t*)k)[ 9]; + ((uint_8t*)d)[10] = ((uint_8t*)s)[10] ^ ((uint_8t*)k)[10]; + ((uint_8t*)d)[11] = ((uint_8t*)s)[11] ^ ((uint_8t*)k)[11]; + ((uint_8t*)d)[12] = ((uint_8t*)s)[12] ^ ((uint_8t*)k)[12]; + ((uint_8t*)d)[13] = ((uint_8t*)s)[13] ^ ((uint_8t*)k)[13]; + ((uint_8t*)d)[14] = ((uint_8t*)s)[14] ^ ((uint_8t*)k)[14]; + ((uint_8t*)d)[15] = ((uint_8t*)s)[15] ^ ((uint_8t*)k)[15]; +#else + block_copy(d, s); + xor_block(d, k); +#endif +} + +static void add_round_key( uint_8t d[N_BLOCK], const uint_8t k[N_BLOCK] ) +{ + xor_block(d, k); +} + +static void shift_sub_rows( uint_8t st[N_BLOCK] ) +{ uint_8t tt; + + st[ 0] = s_box(st[ 0]); st[ 4] = s_box(st[ 4]); + st[ 8] = s_box(st[ 8]); st[12] = s_box(st[12]); + + tt = st[1]; st[ 1] = s_box(st[ 5]); st[ 5] = s_box(st[ 9]); + st[ 9] = s_box(st[13]); st[13] = s_box( tt ); + + tt = st[2]; st[ 2] = s_box(st[10]); st[10] = s_box( tt ); + tt = st[6]; st[ 6] = s_box(st[14]); st[14] = s_box( tt ); + + tt = st[15]; st[15] = s_box(st[11]); st[11] = s_box(st[ 7]); + st[ 7] = s_box(st[ 3]); st[ 3] = s_box( tt ); +} + +static void inv_shift_sub_rows( uint_8t st[N_BLOCK] ) +{ uint_8t tt; + + st[ 0] = is_box(st[ 0]); st[ 4] = is_box(st[ 4]); + st[ 8] = is_box(st[ 8]); st[12] = is_box(st[12]); + + tt = st[13]; st[13] = is_box(st[9]); st[ 9] = is_box(st[5]); + st[ 5] = is_box(st[1]); st[ 1] = is_box( tt ); + + tt = st[2]; st[ 2] = is_box(st[10]); st[10] = is_box( tt ); + tt = st[6]; st[ 6] = is_box(st[14]); st[14] = is_box( tt ); + + tt = st[3]; st[ 3] = is_box(st[ 7]); st[ 7] = is_box(st[11]); + st[11] = is_box(st[15]); st[15] = is_box( tt ); +} + +#if defined( VERSION_1 ) + static void mix_sub_columns( uint_8t dt[N_BLOCK] ) + { uint_8t st[N_BLOCK]; + block_copy(st, dt); +#else + static void mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] ) + { +#endif + dt[ 0] = gfm2_sb(st[0]) ^ gfm3_sb(st[5]) ^ s_box(st[10]) ^ s_box(st[15]); + dt[ 1] = s_box(st[0]) ^ gfm2_sb(st[5]) ^ gfm3_sb(st[10]) ^ s_box(st[15]); + dt[ 2] = s_box(st[0]) ^ s_box(st[5]) ^ gfm2_sb(st[10]) ^ gfm3_sb(st[15]); + dt[ 3] = gfm3_sb(st[0]) ^ s_box(st[5]) ^ s_box(st[10]) ^ gfm2_sb(st[15]); + + dt[ 4] = gfm2_sb(st[4]) ^ gfm3_sb(st[9]) ^ s_box(st[14]) ^ s_box(st[3]); + dt[ 5] = s_box(st[4]) ^ gfm2_sb(st[9]) ^ gfm3_sb(st[14]) ^ s_box(st[3]); + dt[ 6] = s_box(st[4]) ^ s_box(st[9]) ^ gfm2_sb(st[14]) ^ gfm3_sb(st[3]); + dt[ 7] = gfm3_sb(st[4]) ^ s_box(st[9]) ^ s_box(st[14]) ^ gfm2_sb(st[3]); + + dt[ 8] = gfm2_sb(st[8]) ^ gfm3_sb(st[13]) ^ s_box(st[2]) ^ s_box(st[7]); + dt[ 9] = s_box(st[8]) ^ gfm2_sb(st[13]) ^ gfm3_sb(st[2]) ^ s_box(st[7]); + dt[10] = s_box(st[8]) ^ s_box(st[13]) ^ gfm2_sb(st[2]) ^ gfm3_sb(st[7]); + dt[11] = gfm3_sb(st[8]) ^ s_box(st[13]) ^ s_box(st[2]) ^ gfm2_sb(st[7]); + + dt[12] = gfm2_sb(st[12]) ^ gfm3_sb(st[1]) ^ s_box(st[6]) ^ s_box(st[11]); + dt[13] = s_box(st[12]) ^ gfm2_sb(st[1]) ^ gfm3_sb(st[6]) ^ s_box(st[11]); + dt[14] = s_box(st[12]) ^ s_box(st[1]) ^ gfm2_sb(st[6]) ^ gfm3_sb(st[11]); + dt[15] = gfm3_sb(st[12]) ^ s_box(st[1]) ^ s_box(st[6]) ^ gfm2_sb(st[11]); + } + +#if defined( VERSION_1 ) + static void inv_mix_sub_columns( uint_8t dt[N_BLOCK] ) + { uint_8t st[N_BLOCK]; + block_copy(st, dt); +#else + static void inv_mix_sub_columns( uint_8t dt[N_BLOCK], uint_8t st[N_BLOCK] ) + { +#endif + dt[ 0] = is_box(gfm_e(st[ 0]) ^ gfm_b(st[ 1]) ^ gfm_d(st[ 2]) ^ gfm_9(st[ 3])); + dt[ 5] = is_box(gfm_9(st[ 0]) ^ gfm_e(st[ 1]) ^ gfm_b(st[ 2]) ^ gfm_d(st[ 3])); + dt[10] = is_box(gfm_d(st[ 0]) ^ gfm_9(st[ 1]) ^ gfm_e(st[ 2]) ^ gfm_b(st[ 3])); + dt[15] = is_box(gfm_b(st[ 0]) ^ gfm_d(st[ 1]) ^ gfm_9(st[ 2]) ^ gfm_e(st[ 3])); + + dt[ 4] = is_box(gfm_e(st[ 4]) ^ gfm_b(st[ 5]) ^ gfm_d(st[ 6]) ^ gfm_9(st[ 7])); + dt[ 9] = is_box(gfm_9(st[ 4]) ^ gfm_e(st[ 5]) ^ gfm_b(st[ 6]) ^ gfm_d(st[ 7])); + dt[14] = is_box(gfm_d(st[ 4]) ^ gfm_9(st[ 5]) ^ gfm_e(st[ 6]) ^ gfm_b(st[ 7])); + dt[ 3] = is_box(gfm_b(st[ 4]) ^ gfm_d(st[ 5]) ^ gfm_9(st[ 6]) ^ gfm_e(st[ 7])); + + dt[ 8] = is_box(gfm_e(st[ 8]) ^ gfm_b(st[ 9]) ^ gfm_d(st[10]) ^ gfm_9(st[11])); + dt[13] = is_box(gfm_9(st[ 8]) ^ gfm_e(st[ 9]) ^ gfm_b(st[10]) ^ gfm_d(st[11])); + dt[ 2] = is_box(gfm_d(st[ 8]) ^ gfm_9(st[ 9]) ^ gfm_e(st[10]) ^ gfm_b(st[11])); + dt[ 7] = is_box(gfm_b(st[ 8]) ^ gfm_d(st[ 9]) ^ gfm_9(st[10]) ^ gfm_e(st[11])); + + dt[12] = is_box(gfm_e(st[12]) ^ gfm_b(st[13]) ^ gfm_d(st[14]) ^ gfm_9(st[15])); + dt[ 1] = is_box(gfm_9(st[12]) ^ gfm_e(st[13]) ^ gfm_b(st[14]) ^ gfm_d(st[15])); + dt[ 6] = is_box(gfm_d(st[12]) ^ gfm_9(st[13]) ^ gfm_e(st[14]) ^ gfm_b(st[15])); + dt[11] = is_box(gfm_b(st[12]) ^ gfm_d(st[13]) ^ gfm_9(st[14]) ^ gfm_e(st[15])); + } + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +/* Set the cipher key for the pre-keyed version */ + +return_type aes_set_key( const unsigned char key[], length_type keylen, aes_context ctx[1] ) +{ + uint_8t cc, rc, hi; + + switch( keylen ) + { + case 16: + case 128: + keylen = 16; + break; + case 24: + case 192: + keylen = 24; + break; + case 32: + case 256: + keylen = 32; + break; + default: + ctx->rnd = 0; + return (return_type)-1; + } + block_copy_nn(ctx->ksch, key, keylen); + hi = (keylen + 28) << 2; + ctx->rnd = (hi >> 4) - 1; + for( cc = keylen, rc = 1; cc < hi; cc += 4 ) + { uint_8t tt, t0, t1, t2, t3; + + t0 = ctx->ksch[cc - 4]; + t1 = ctx->ksch[cc - 3]; + t2 = ctx->ksch[cc - 2]; + t3 = ctx->ksch[cc - 1]; + if( cc % keylen == 0 ) + { + tt = t0; + t0 = s_box(t1) ^ rc; + t1 = s_box(t2); + t2 = s_box(t3); + t3 = s_box(tt); + rc = f2(rc); + } + else if( keylen > 24 && cc % keylen == 16 ) + { + t0 = s_box(t0); + t1 = s_box(t1); + t2 = s_box(t2); + t3 = s_box(t3); + } + tt = cc - keylen; + ctx->ksch[cc + 0] = ctx->ksch[tt + 0] ^ t0; + ctx->ksch[cc + 1] = ctx->ksch[tt + 1] ^ t1; + ctx->ksch[cc + 2] = ctx->ksch[tt + 2] ^ t2; + ctx->ksch[cc + 3] = ctx->ksch[tt + 3] ^ t3; + } + return 0; +} + +#endif + +#if defined( AES_ENC_PREKEYED ) + +/* Encrypt a single block of 16 bytes */ + +return_type aes_encrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint_8t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch ); + + for( r = 1 ; r < ctx->rnd ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + add_round_key( s1, ctx->ksch + r * N_BLOCK); + } +#else + { uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + copy_and_key( s1, s2, ctx->ksch + r * N_BLOCK); + } +#endif + shift_sub_rows( s1 ); + copy_and_key( out, s1, ctx->ksch + r * N_BLOCK ); + } + else + return (return_type)-1; + return 0; +} + +/* CBC encrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_encrypt( const unsigned char *in, unsigned char *out, + int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + + while(n_block--) + { + xor_block(iv, in); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + memcpy(out, iv, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_DEC_PREKEYED ) + +/* Decrypt a single block of 16 bytes */ + +return_type aes_decrypt( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], const aes_context ctx[1] ) +{ + if( ctx->rnd ) + { + uint_8t s1[N_BLOCK], r; + copy_and_key( s1, in, ctx->ksch + ctx->rnd * N_BLOCK ); + inv_shift_sub_rows( s1 ); + + for( r = ctx->rnd ; --r ; ) +#if defined( VERSION_1 ) + { + add_round_key( s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1 ); + } +#else + { uint_8t s2[N_BLOCK]; + copy_and_key( s2, s1, ctx->ksch + r * N_BLOCK ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, ctx->ksch ); + } + else + return (return_type)-1; + return 0; +} + +/* CBC decrypt a number of blocks (input and return an IV) */ + +return_type aes_cbc_decrypt( const unsigned char *in, unsigned char *out, + int n_block, unsigned char iv[N_BLOCK], const aes_context ctx[1] ) +{ + while(n_block--) + { uint_8t tmp[N_BLOCK]; + + memcpy(tmp, in, N_BLOCK); + if(aes_decrypt(in, out, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + xor_block(out, iv); + memcpy(iv, tmp, N_BLOCK); + in += N_BLOCK; + out += N_BLOCK; + } + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_ENC_128_OTFK ) + +/* The 'on the fly' encryption key update for for 128 bit keys */ + +static void update_encrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc ) +{ uint_8t cc; + + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_encrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] ) +{ uint_8t s1[N_BLOCK], r, rc = 1; + + if(o_key != key) + block_copy( o_key, key ); + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 10 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns( s1 ); + update_encrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + } +#else + { uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_128_OTFK ) + +/* The 'on the fly' decryption key update for for 128 bit keys */ + +static void update_decrypt_key_128( uint_8t k[N_BLOCK], uint_8t *rc ) +{ uint_8t cc; + + for( cc = 12; cc > 0; cc -= 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + *rc = d2(*rc); + k[0] ^= s_box(k[13]) ^ *rc; + k[1] ^= s_box(k[14]); + k[2] ^= s_box(k[15]); + k[3] ^= s_box(k[12]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' 128 bit keying */ + +void aes_decrypt_128( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], unsigned char o_key[N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 0x6c; + if(o_key != key) + block_copy( o_key, key ); + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 10 ; --r ; ) +#if defined( VERSION_1 ) + { + update_decrypt_key_128( o_key, &rc ); + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint_8t s2[N_BLOCK]; + update_decrypt_key_128( o_key, &rc ); + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + update_decrypt_key_128( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_ENC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_encrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc ) +{ uint_8t cc; + + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); + *rc = f2( *rc ); + + for(cc = 4; cc < 16; cc += 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for( cc = 20; cc < 32; cc += 4 ) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } +} + +/* Encrypt a single block of 16 bytes with 'on the fly' 256 bit keying */ + +void aes_encrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 1; + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + copy_and_key( s1, in, o_key ); + + for( r = 1 ; r < 14 ; ++r ) +#if defined( VERSION_1 ) + { + mix_sub_columns(s1); + if( r & 1 ) + add_round_key( s1, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key ); + } + } +#else + { uint_8t s2[N_BLOCK]; + mix_sub_columns( s2, s1 ); + if( r & 1 ) + copy_and_key( s1, s2, o_key + 16 ); + else + { + update_encrypt_key_256( o_key, &rc ); + copy_and_key( s1, s2, o_key ); + } + } +#endif + + shift_sub_rows( s1 ); + update_encrypt_key_256( o_key, &rc ); + copy_and_key( out, s1, o_key ); +} + +#endif + +#if defined( AES_DEC_256_OTFK ) + +/* The 'on the fly' encryption key update for for 256 bit keys */ + +static void update_decrypt_key_256( uint_8t k[2 * N_BLOCK], uint_8t *rc ) +{ uint_8t cc; + + for(cc = 28; cc > 16; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + k[16] ^= s_box(k[12]); + k[17] ^= s_box(k[13]); + k[18] ^= s_box(k[14]); + k[19] ^= s_box(k[15]); + + for(cc = 12; cc > 0; cc -= 4) + { + k[cc + 0] ^= k[cc - 4]; + k[cc + 1] ^= k[cc - 3]; + k[cc + 2] ^= k[cc - 2]; + k[cc + 3] ^= k[cc - 1]; + } + + *rc = d2(*rc); + k[0] ^= s_box(k[29]) ^ *rc; + k[1] ^= s_box(k[30]); + k[2] ^= s_box(k[31]); + k[3] ^= s_box(k[28]); +} + +/* Decrypt a single block of 16 bytes with 'on the fly' + 256 bit keying +*/ +void aes_decrypt_256( const unsigned char in[N_BLOCK], unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], unsigned char o_key[2 * N_BLOCK] ) +{ + uint_8t s1[N_BLOCK], r, rc = 0x80; + + if(o_key != key) + { + block_copy( o_key, key ); + block_copy( o_key + 16, key + 16 ); + } + + copy_and_key( s1, in, o_key ); + inv_shift_sub_rows( s1 ); + + for( r = 14 ; --r ; ) +#if defined( VERSION_1 ) + { + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + add_round_key( s1, o_key + 16 ); + } + else + add_round_key( s1, o_key ); + inv_mix_sub_columns( s1 ); + } +#else + { uint_8t s2[N_BLOCK]; + if( ( r & 1 ) ) + { + update_decrypt_key_256( o_key, &rc ); + copy_and_key( s2, s1, o_key + 16 ); + } + else + copy_and_key( s2, s1, o_key ); + inv_mix_sub_columns( s1, s2 ); + } +#endif + copy_and_key( out, s1, o_key ); +} + +#endif diff --git a/stack/smp/aes.h b/stack/smp/aes.h new file mode 100644 index 0000000..dcfde93 --- /dev/null +++ b/stack/smp/aes.h @@ -0,0 +1,162 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue 09/09/2006 + + This is an AES implementation that uses only 8-bit byte operations on the + cipher state. + */ + +#ifndef AES_H +#define AES_H + +#if 1 +# define AES_ENC_PREKEYED /* AES encryption with a precomputed key schedule */ +#endif +#if 1 +# define AES_DEC_PREKEYED /* AES decryption with a precomputed key schedule */ +#endif +#if 1 +# define AES_ENC_128_OTFK /* AES encryption with 'on the fly' 128 bit keying */ +#endif +#if 1 +# define AES_DEC_128_OTFK /* AES decryption with 'on the fly' 128 bit keying */ +#endif +#if 1 +# define AES_ENC_256_OTFK /* AES encryption with 'on the fly' 256 bit keying */ +#endif +#if 1 +# define AES_DEC_256_OTFK /* AES decryption with 'on the fly' 256 bit keying */ +#endif + +#define N_ROW 4 +#define N_COL 4 +#define N_BLOCK (N_ROW * N_COL) +#define N_MAX_ROUNDS 14 + +typedef unsigned char uint_8t; + +typedef uint_8t return_type; + +/* Warning: The key length for 256 bit keys overflows a byte + (see comment below) +*/ + +typedef uint_8t length_type; + +typedef struct +{ uint_8t ksch[(N_MAX_ROUNDS + 1) * N_BLOCK]; + uint_8t rnd; +} aes_context; + +/* The following calls are for a precomputed key schedule + + NOTE: If the length_type used for the key length is an + unsigned 8-bit character, a key length of 256 bits must + be entered as a length in bytes (valid inputs are hence + 128, 192, 16, 24 and 32). +*/ + +#if defined( AES_ENC_PREKEYED ) || defined( AES_DEC_PREKEYED ) + +return_type aes_set_key( const unsigned char key[], + length_type keylen, + aes_context ctx[1] ); +#endif + +#if defined( AES_ENC_PREKEYED ) + +return_type aes_encrypt( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_encrypt( const unsigned char *in, + unsigned char *out, + int n_block, + unsigned char iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +#if defined( AES_DEC_PREKEYED ) + +return_type aes_decrypt( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const aes_context ctx[1] ); + +return_type aes_cbc_decrypt( const unsigned char *in, + unsigned char *out, + int n_block, + unsigned char iv[N_BLOCK], + const aes_context ctx[1] ); +#endif + +/* The following calls are for 'on the fly' keying. In this case the + encryption and decryption keys are different. + + The encryption subroutines take a key in an array of bytes in + key[L] where L is 16, 24 or 32 bytes for key lengths of 128, + 192, and 256 bits respectively. They then encrypts the input + data, in[] with this key and put the reult in the output array + out[]. In addition, the second key array, o_key[L], is used + to output the key that is needed by the decryption subroutine + to reverse the encryption operation. The two key arrays can + be the same array but in this case the original key will be + overwritten. + + In the same way, the decryption subroutines output keys that + can be used to reverse their effect when used for encryption. + + Only 128 and 256 bit keys are supported in these 'on the fly' + modes. +*/ + +#if defined( AES_ENC_128_OTFK ) +void aes_encrypt_128( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], + uint_8t o_key[N_BLOCK] ); +#endif + +#if defined( AES_DEC_128_OTFK ) +void aes_decrypt_128( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[N_BLOCK], + unsigned char o_key[N_BLOCK] ); +#endif + +#if defined( AES_ENC_256_OTFK ) +void aes_encrypt_256( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], + unsigned char o_key[2 * N_BLOCK] ); +#endif + +#if defined( AES_DEC_256_OTFK ) +void aes_decrypt_256( const unsigned char in[N_BLOCK], + unsigned char out[N_BLOCK], + const unsigned char key[2 * N_BLOCK], + unsigned char o_key[2 * N_BLOCK] ); +#endif + +#endif diff --git a/stack/smp/smp_act.c b/stack/smp/smp_act.c new file mode 100644 index 0000000..ca24225 --- /dev/null +++ b/stack/smp/smp_act.c @@ -0,0 +1,950 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + + #include + #include "btm_int.h" + #include "l2c_api.h" + #include "smp_int.h" + + +const UINT8 smp_association_table[2][SMP_IO_CAP_MAX][SMP_IO_CAP_MAX] = +{ + /* initiator */ + {{SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY}, /* Display Only */ + {SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY}, /* SMP_CAP_IO = 1 */ + {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_KEY_NOTIF}, /* keyboard only */ + {SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY},/* No Input No Output */ + {SMP_MODEL_KEY_NOTIF, SMP_MODEL_KEY_NOTIF, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_KEY_NOTIF}}, /* keyboard display */ + /* responder */ + {{SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENC_ONLY, SMP_MODEL_KEY_NOTIF}, /* Display Only */ + {SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_KEY_NOTIF}, /* SMP_CAP_IO = 1 */ + {SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY}, /* keyboard only */ + {SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY, SMP_MODEL_ENC_ONLY},/* No Input No Output */ + {SMP_MODEL_PASSKEY, SMP_MODEL_PASSKEY, SMP_MODEL_KEY_NOTIF, SMP_MODEL_ENC_ONLY, SMP_MODEL_PASSKEY}} /* keyboard display */ + /* display only */ /*SMP_CAP_IO = 1 */ /* keyboard only */ /* No InputOutput */ /* keyboard display */ +}; + +const tSMP_ACT smp_distribute_act [] = +{ + smp_generate_ltk, + smp_send_id_info, + smp_generate_csrk +}; + +/******************************************************************************* +** Function smp_update_key_mask +** Description This function updates the key mask for sending or receiving. +*******************************************************************************/ +static void smp_update_key_mask (tSMP_CB *p_cb, UINT8 key_type, BOOLEAN recv) +{ + SMP_TRACE_DEBUG0 ("smp_update_key_mask "); + SMP_TRACE_DEBUG4("before update role=%d recv=%d loc_i_key = %02x, loc_r_key = %02x", p_cb->role, recv, p_cb->loc_i_key, p_cb->loc_r_key); + if (p_cb->role == HCI_ROLE_SLAVE) + { + if (recv) + p_cb->loc_i_key &= ~key_type; + else + p_cb->loc_r_key &= ~key_type; + } + else + { + if (recv) + p_cb->loc_r_key &= ~key_type; + else + p_cb->loc_i_key &= ~key_type; + } + + SMP_TRACE_DEBUG2("updated loc_i_key = %02x, loc_r_key = %02x", p_cb->loc_i_key, p_cb->loc_r_key); +} +/******************************************************************************* +** Function smp_io_cap_req +** Description send SMP IO request +*******************************************************************************/ +void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tSMP_EVT_DATA cb_data; + tSMP_STATUS callback_rc; + SMP_TRACE_DEBUG1 ("smp_send_app_cback p_cb->cb_evt=%d", p_cb->cb_evt ); + if (p_cb->p_callback && p_cb->cb_evt != 0) + { + if (p_cb->cb_evt == SMP_IO_CAP_REQ_EVT) + { + cb_data.io_req.auth_req = p_cb->peer_auth_req; + cb_data.io_req.oob_data = SMP_OOB_NONE; + cb_data.io_req.io_cap = SMP_DEFAULT_IO_CAPS; + cb_data.io_req.max_key_size = SMP_MAX_ENC_KEY_SIZE; + cb_data.io_req.init_keys = p_cb->loc_i_key ; + cb_data.io_req.resp_keys = p_cb->loc_r_key ; + + SMP_TRACE_WARNING1( "io_cap = %d",cb_data.io_req.io_cap); + } + callback_rc = (*p_cb->p_callback)(p_cb->cb_evt, p_cb->pairing_bda, &cb_data); + + SMP_TRACE_DEBUG2 ("callback_rc=%d p_cb->cb_evt=%d",callback_rc, p_cb->cb_evt ); + + if (callback_rc == SMP_SUCCESS && p_cb->cb_evt == SMP_IO_CAP_REQ_EVT) + { + p_cb->loc_auth_req = cb_data.io_req.auth_req; + p_cb->loc_io_caps = cb_data.io_req.io_cap; + p_cb->loc_oob_flag = cb_data.io_req.oob_data; + p_cb->loc_enc_size = cb_data.io_req.max_key_size; + p_cb->loc_i_key = cb_data.io_req.init_keys; + p_cb->loc_r_key = cb_data.io_req.resp_keys; + + SMP_TRACE_WARNING2( "new io_cap = %d p_cb->loc_enc_size = %d",p_cb->loc_io_caps, p_cb->loc_enc_size); + + smp_sm_event(p_cb, SMP_IO_RSP_EVT, NULL); + } + } + + if (!p_cb->cb_evt && p_cb->discard_sec_req) + { + p_cb->discard_sec_req = FALSE; + smp_sm_event(p_cb, SMP_DISCARD_SEC_REQ_EVT, NULL); + } + SMP_TRACE_DEBUG0 ("smp_send_app_cback return"); +} +/******************************************************************************* +** Function smp_send_pair_fail +** Description pairing failure to peer device if needed. +*******************************************************************************/ +void smp_send_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + p_cb->status = *(UINT8 *)p_data; + p_cb->failure = *(UINT8 *)p_data; + + SMP_TRACE_DEBUG2 ("smp_send_pair_fail status=%d failure=%d ",p_cb->status, p_cb->failure); + + if (p_cb->status <= SMP_REPEATED_ATTEMPTS && p_cb->status != SMP_SUCCESS) + { + smp_send_cmd(SMP_OPCODE_PAIRING_FAILED, p_cb); + } +} + +/******************************************************************************* +** Function smp_send_pair_req +** Description process pairing request to slave device +*******************************************************************************/ +void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); + SMP_TRACE_DEBUG0 ("smp_send_pair_req "); + + /* erase all keys when master sends pairing req*/ + if (p_dev_rec) + btm_sec_clear_ble_keys(p_dev_rec); + /* do not manipulate the key, let app decide, + leave out to BTM to mandate key distribution for bonding case */ + smp_send_cmd(SMP_OPCODE_PAIRING_REQ, p_cb); +} +/******************************************************************************* +** Function smp_send_pair_rsp +** Description process pairing response to slave device +*******************************************************************************/ +void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_send_pair_rsp "); + + p_cb->loc_i_key &= p_cb->peer_i_key; + p_cb->loc_r_key &= p_cb->peer_r_key; + + if (smp_send_cmd (SMP_OPCODE_PAIRING_RSP, p_cb)) + { + smp_decide_asso_model(p_cb, NULL); + } +} + +/******************************************************************************* +** Function smp_send_pair_request +** Description process pairing request to slave device +*******************************************************************************/ +void smp_send_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_send_confirm "); + smp_send_cmd(SMP_OPCODE_CONFIRM, p_cb); +} +/******************************************************************************* +** Function smp_send_init +** Description process pairing initializer to slave device +*******************************************************************************/ +void smp_send_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_send_init "); + +#if SMP_CONFORMANCE_TESTING == TRUE + if (p_cb->enable_test_rand_val) + { + SMP_TRACE_DEBUG0 ("Use rand value from script"); + memcpy(p_cb->rand, p_cb->test_rand, BT_OCTET16_LEN); + } +#endif + + smp_send_cmd(SMP_OPCODE_INIT, p_cb); +} +/******************************************************************************* +** Function smp_send_enc_info +** Description send security information command. +*******************************************************************************/ +void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_LENC_KEYS le_key; + + SMP_TRACE_DEBUG1 ("smp_send_enc_info p_cb->loc_enc_size = %d", p_cb->loc_enc_size); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, FALSE); + + smp_send_cmd(SMP_OPCODE_ENCRYPT_INFO, p_cb); + smp_send_cmd(SMP_OPCODE_MASTER_ID, p_cb); + + /* save the DIV and key size information when acting as slave device */ + le_key.div = p_cb->div; + le_key.key_size = p_cb->loc_enc_size; + le_key.sec_level = p_cb->sec_level; + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LENC, (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + + SMP_TRACE_WARNING0( "smp_send_enc_info"); + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_send_id_info +** Description send ID information command. +*******************************************************************************/ +void smp_send_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_send_id_info "); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, FALSE); + + smp_send_cmd(SMP_OPCODE_IDENTITY_INFO, p_cb); + smp_send_cmd(SMP_OPCODE_ID_ADDR, p_cb); + + SMP_TRACE_WARNING0( "smp_send_id_info"); + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_send_csrk_info +** Description send CSRK command. +*******************************************************************************/ +void smp_send_csrk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_KEY_VALUE key; + SMP_TRACE_DEBUG0 ("smp_send_csrk_info "); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_CSRK, FALSE); + + if (smp_send_cmd(SMP_OPCODE_SIGN_INFO, p_cb)) + { + key.lcsrk_key.div = p_cb->div; + key.lcsrk_key.sec_level = p_cb->sec_level; + key.lcsrk_key.counter = 0; /* initialize the local counter */ + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LCSRK, &key, TRUE); + } + + smp_key_distribution(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_send_ltk_reply +** Description send LTK reply +*******************************************************************************/ +void smp_send_ltk_reply(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_send_ltk_reply "); + /* send stk as LTK response */ + btm_ble_ltk_request_reply(p_cb->pairing_bda, TRUE, p_data->key.p_data); +} +/******************************************************************************* +** Function smp_proc_sec_req +** Description process security request. +*******************************************************************************/ +void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_AUTH_REQ auth_req = *(tBTM_LE_AUTH_REQ *)p_data; + tBTM_BLE_SEC_REQ_ACT sec_req_act; + + + SMP_TRACE_DEBUG1 ("smp_proc_sec_req auth_req=0x%x",auth_req); + + p_cb->cb_evt = 0; + + btm_ble_link_sec_check(p_cb->pairing_bda, auth_req, &sec_req_act); + + SMP_TRACE_DEBUG1 ("smp_proc_sec_req sec_req_act=0x%x",sec_req_act); + + switch (sec_req_act) + { + case BTM_BLE_SEC_REQ_ACT_ENCRYPT: + SMP_TRACE_DEBUG0 ("smp_proc_sec_req BTM_BLE_SEC_REQ_ACT_ENCRYPT"); + smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); + break; + + case BTM_BLE_SEC_REQ_ACT_PAIR: + /* initialize local i/r key to be default keys */ + SMP_TRACE_DEBUG0 ("smp_proc_sec_req BTM_BLE_SEC_REQ_ACT_PAIR"); + p_cb->peer_auth_req = auth_req; + p_cb->loc_r_key = p_cb->loc_i_key = SMP_SEC_DEFAULT_KEY ; + p_cb->cb_evt = SMP_SEC_REQUEST_EVT; + break; + + case BTM_BLE_SEC_REQ_ACT_DISCARD: + p_cb->discard_sec_req = TRUE; + break; + + default: + /* do nothing */ + break; + } +} +/******************************************************************************* +** Function smp_proc_sec_grant +** Description process security grant. +*******************************************************************************/ +void smp_proc_sec_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 res= *(UINT8 *)p_data; + SMP_TRACE_DEBUG0 ("smp_proc_sec_grant "); + if (res != SMP_SUCCESS) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, p_data); + } + else /*otherwise, start pairing */ + { + /* send IO request callback */ + p_cb->cb_evt = SMP_IO_CAP_REQ_EVT; + } +} +/******************************************************************************* +** Function smp_proc_pair_fail +** Description process pairing failure from peer device +*******************************************************************************/ +void smp_proc_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_proc_pair_fail "); + p_cb->status = *(UINT8 *)p_data; +} +/******************************************************************************* +** Function smp_proc_pair_cmd +** Description Process the SMP pairing request/response from peer device +*******************************************************************************/ +void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + UINT8 reason = SMP_ENC_KEY_SIZE; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_cb->pairing_bda); + + SMP_TRACE_DEBUG0 ("smp_proc_pair_cmd "); + /* erase all keys if it is slave proc pairing req*/ + if (p_dev_rec && (p_cb->role == HCI_ROLE_SLAVE)) + btm_sec_clear_ble_keys(p_dev_rec); + + p_cb->flags |= SMP_PAIR_FLAG_ENC_AFTER_PAIR; + + STREAM_TO_UINT8(p_cb->peer_io_caps, p); + STREAM_TO_UINT8(p_cb->peer_oob_flag, p); + STREAM_TO_UINT8(p_cb->peer_auth_req, p); + STREAM_TO_UINT8(p_cb->peer_enc_size, p); + STREAM_TO_UINT8(p_cb->peer_i_key, p); + STREAM_TO_UINT8(p_cb->peer_r_key, p); + +#if SMP_CONFORMANCE_TESTING == TRUE + if (p_cb->enable_test_pair_fail) + { + SMP_TRACE_DEBUG0 ("Forced pair fair"); + if (p_cb->peer_enc_size < SMP_MIN_ENC_KEY_SIZE) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + else + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &(p_cb->pair_fail_status)); + } + return; + } +#endif + + + if (p_cb->peer_enc_size < SMP_MIN_ENC_KEY_SIZE) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + else if (p_cb->role == HCI_ROLE_SLAVE) + { + if (!(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) + { + p_cb->loc_i_key = p_cb->peer_i_key; + p_cb->loc_r_key = p_cb->peer_r_key; + } + else /* update local i/r key according to pairing request */ + { + p_cb->loc_i_key &= p_cb->peer_i_key; + p_cb->loc_r_key &= p_cb->peer_r_key; + } + + p_cb->cb_evt = SMP_SEC_REQUEST_EVT; + } +} +/******************************************************************************* +** Function smp_proc_confirm +** Description process pairing confirm from peer device +*******************************************************************************/ +void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + + SMP_TRACE_DEBUG0 ("smp_proc_confirm "); + if (p != NULL) + { + /* save the SConfirm for comparison later */ + STREAM_TO_ARRAY(p_cb->rconfirm, p, BT_OCTET16_LEN); + } + + p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM; +} + +/******************************************************************************* +** Function smp_proc_init +** Description process pairing initializer from peer device +*******************************************************************************/ +void smp_proc_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + SMP_TRACE_DEBUG0 ("smp_proc_init "); + /* save the SRand for comparison */ + STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN); + +} +/******************************************************************************* +** Function smp_proc_enc_info +** Description process encryption information from peer device +*******************************************************************************/ +void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + + SMP_TRACE_DEBUG0 ("smp_proc_enc_info "); + STREAM_TO_ARRAY(p_cb->ltk, p, BT_OCTET16_LEN); + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_proc_master_id +** Description process master ID from slave device +*******************************************************************************/ +void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + tBTM_LE_PENC_KEYS le_key; + + SMP_TRACE_DEBUG0 (" smp_proc_master_id"); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ENC, TRUE); + + STREAM_TO_UINT16(le_key.ediv, p); + STREAM_TO_ARRAY(le_key.rand, p, BT_OCTET8_LEN ); + + /* store the encryption keys from peer device */ + memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN); + le_key.sec_level = p_cb->sec_level; + le_key.key_size = p_cb->loc_enc_size; + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PENC, (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_proc_enc_info +** Description process identity information from peer device +*******************************************************************************/ +void smp_proc_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + + SMP_TRACE_DEBUG0 ("smp_proc_id_info "); + STREAM_TO_ARRAY (p_cb->tk, p, BT_OCTET16_LEN); /* reuse TK for IRK */ + /* store the ID key from peer device */ + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, (tBTM_LE_KEY_VALUE *)&p_cb->tk, TRUE); + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_proc_id_addr +** Description process identity address from peer device +*******************************************************************************/ +void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + BD_ADDR mac_bda; + + SMP_TRACE_DEBUG0 ("smp_proc_id_addr "); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_ID, TRUE); + + STREAM_TO_BDADDR(mac_bda, p); + + /* TODO, update MAC address */ + + smp_key_distribution(p_cb, NULL); +} +/******************************************************************************* +** Function smp_proc_srk_info +** Description process security information from peer device +*******************************************************************************/ +void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + tBTM_LE_PCSRK_KEYS le_key; + + SMP_TRACE_DEBUG0 ("smp_proc_srk_info "); + smp_update_key_mask (p_cb, SMP_SEC_KEY_TYPE_CSRK, TRUE); + + /* save CSRK to security record */ + le_key.sec_level = p_cb->sec_level; + memcpy (le_key.csrk, p_data, BT_OCTET16_LEN); /* get peer CSRK */ + le_key.counter = 0; /* initialize the peer counter */ + btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PCSRK, (tBTM_LE_KEY_VALUE *)&le_key, TRUE); + + smp_key_distribution(p_cb, NULL); +} + +/******************************************************************************* +** Function smp_proc_compare +** Description process compare value +*******************************************************************************/ +void smp_proc_compare(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason; + + SMP_TRACE_DEBUG0 ("smp_proc_compare "); + if (!memcmp(p_cb->rconfirm, p_data->key.p_data, BT_OCTET16_LEN)) + { + /* compare the max encryption key size, and save the smaller one for the link */ + if ( p_cb->peer_enc_size < p_cb->loc_enc_size) + p_cb->loc_enc_size = p_cb->peer_enc_size; + + if (p_cb->role == HCI_ROLE_SLAVE) + smp_sm_event(p_cb, SMP_RAND_EVT, NULL); + else + { + /* master device always use received i/r key as keys to distribute */ + p_cb->loc_i_key = p_cb->peer_i_key; + p_cb->loc_r_key = p_cb->peer_r_key; + + smp_sm_event(p_cb, SMP_ENC_REQ_EVT, NULL); + } + + } + else + { + reason = p_cb->failure = SMP_CONFIRM_VALUE_ERR; + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } +} +/******************************************************************************* +** Function smp_proc_sl_key +** Description process key ready events. +*******************************************************************************/ +void smp_proc_sl_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 key_type = p_data->key.key_type; + + SMP_TRACE_DEBUG0 ("smp_proc_sl_keysmp_proc_sl_key "); + if (key_type == SMP_KEY_TYPE_TK) + { + smp_generate_confirm(p_cb, NULL); + } + else if (key_type == SMP_KEY_TYPE_CFM) + { + smp_set_state(SMP_ST_WAIT_CONFIRM); + + if (p_cb->flags & SMP_PAIR_FLAGS_CMD_CONFIRM) + smp_sm_event(p_cb, SMP_CONFIRM_EVT, NULL); + + } +} +/******************************************************************************* +** Function smp_start_enc +** Description start encryption +*******************************************************************************/ +void smp_start_enc(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BOOLEAN cmd; + UINT8 reason = SMP_ENC_FAIL; + + SMP_TRACE_DEBUG0 ("smp_start_enc "); + if (p_data != NULL) + cmd = btm_ble_start_encrypt(p_cb->pairing_bda, TRUE, p_data->key.p_data); + else + cmd = btm_ble_start_encrypt(p_cb->pairing_bda, FALSE, NULL); + + if (!cmd) + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + +} + +/******************************************************************************* +** Function smp_proc_discard +** Description processing for discard security request +*******************************************************************************/ +void smp_proc_discard(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_proc_discard "); + smp_cb_cleanup(p_cb); +} +/******************************************************************************* +** Function smp_proc_release_delay +** Description process the release delay request +*******************************************************************************/ +void smp_proc_release_delay(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_proc_release_delay "); + btu_stop_timer (&p_cb->rsp_timer_ent); + btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, + SMP_WAIT_FOR_REL_DELAY_TOUT); +} + +/******************************************************************************* +** Function smp_proc_release_delay_tout +** Description processing the release delay timeout +*******************************************************************************/ +void smp_proc_release_delay_tout(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_proc_release_delay_tout "); + btu_stop_timer (&p_cb->rsp_timer_ent); + smp_proc_pairing_cmpl(p_cb); +} + + +/******************************************************************************* +** Function smp_enc_cmpl +** Description encryption success +*******************************************************************************/ +void smp_enc_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 enc_enable = *(UINT8 *)p_data; + UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; + + SMP_TRACE_DEBUG0 ("smp_enc_cmpl "); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); +} + + +/******************************************************************************* +** Function smp_check_auth_req +** Description check authentication request +*******************************************************************************/ +void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 enc_enable = *(UINT8 *)p_data; + UINT8 reason = enc_enable ? SMP_SUCCESS : SMP_ENC_FAIL; + + SMP_TRACE_DEBUG3 ("smp_check_auth_req enc_enable=%d i_keys=0x%x r_keys=0x%x (i-initiator r-responder)", + enc_enable, p_cb->loc_i_key, p_cb->loc_r_key); + if (enc_enable == 1) + { + if (/*((p_cb->peer_auth_req & SMP_AUTH_BOND) || + (p_cb->loc_auth_req & SMP_AUTH_BOND)) &&*/ + (p_cb->loc_i_key || p_cb->loc_r_key)) + { + smp_sm_event(p_cb, SMP_BOND_REQ_EVT, NULL); + } + else + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + else if (enc_enable == 0) + { + /* if failed for encryption after pairing, send callback */ + if (p_cb->flags & SMP_PAIR_FLAG_ENC_AFTER_PAIR) + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + /* if enc failed for old security information */ + /* if master device, clean up and abck to idle; slave device do nothing */ + else if (p_cb->role == HCI_ROLE_MASTER) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } + } +} + +/******************************************************************************* +** Function smp_key_pick_key +** Description Pick a key distribution function based on the key mask. +*******************************************************************************/ +void smp_key_pick_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 key_to_dist = (p_cb->role == HCI_ROLE_SLAVE) ? p_cb->loc_r_key : p_cb->loc_i_key; + UINT8 i = 0; + + SMP_TRACE_DEBUG1 ("smp_key_pick_key key_to_dist=0x%x", key_to_dist); + while (i < 3) + { + SMP_TRACE_DEBUG2("key to send = %02x, i = %d", key_to_dist, i); + + if (key_to_dist & (1 << i)) + { + SMP_TRACE_DEBUG1 ("smp_distribute_act[%d]", i); + (* smp_distribute_act[i])(p_cb, p_data); + break; + } + i ++; + } +} +/******************************************************************************* +** Function smp_key_distribution +** Description start key distribution if required. +*******************************************************************************/ +void smp_key_distribution(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 reason = SMP_SUCCESS; + SMP_TRACE_DEBUG3 ("smp_key_distribution role=%d (0-master) r_keys=0x%x i_keys=0x%x", + p_cb->role, p_cb->loc_r_key, p_cb->loc_i_key); + + if (p_cb->role == HCI_ROLE_SLAVE|| + (!p_cb->loc_r_key && p_cb->role == HCI_ROLE_MASTER)) + { + smp_key_pick_key(p_cb, p_data); + } + + if (!p_cb->loc_i_key && !p_cb->loc_r_key) + { + /* state check to prevent re-entrant */ + if (smp_get_state() == SMP_ST_BOND_PENDING) + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &reason); + } +} +/******************************************************************************* +** Function smp_decide_asso_model +** Description This function is called to compare both sides' io capability +** oob data flag and authentication request, and decide the +** association model to use for the authentication. +*******************************************************************************/ +void smp_decide_asso_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + UINT8 failure = SMP_UNKNOWN_IO_CAP; + tSMP_ASSO_MODEL model = SMP_MODEL_MAX; + UINT8 int_evt = 0; + tSMP_KEY key; + tSMP_INT_DATA *p; + + SMP_TRACE_DEBUG3 ("smp_decide_asso_model p_cb->peer_io_caps = %d p_cb->loc_io_caps = %d \ + p_cb->peer_auth_req = %02x", + p_cb->peer_io_caps, p_cb->loc_io_caps, p_cb->peer_auth_req); + + /* OOB data present on both devices, use OOB association model */ + if (p_cb->peer_oob_flag == SMP_OOB_PRESENT && p_cb->loc_oob_flag == SMP_OOB_PRESENT) + { + model = SMP_MODEL_OOB; + } + /* no MITM required, ignore IO cap, use encryption only */ + else if (SMP_NO_MITM_REQUIRED (p_cb->peer_auth_req) && + SMP_NO_MITM_REQUIRED(p_cb->loc_auth_req)) + { + model = SMP_MODEL_ENC_ONLY; + } + else/* use IO capability to decide assiciation model */ + { + if (p_cb->peer_io_caps < SMP_IO_CAP_MAX && p_cb->loc_io_caps < SMP_IO_CAP_MAX) + { + if (p_cb->role == HCI_ROLE_MASTER) + model = smp_association_table[p_cb->role][p_cb->peer_io_caps][p_cb->loc_io_caps]; + else + model = smp_association_table[p_cb->role][p_cb->loc_io_caps][p_cb->peer_io_caps]; + } + } + + SMP_TRACE_DEBUG1("Association Model = %d", model); + + if (model == SMP_MODEL_OOB) + { + SMP_TRACE_ERROR0("Association Model = SMP_MODEL_OOB"); + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_EVENT1 ("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) ", p_cb->sec_level ); + p_cb->cb_evt = SMP_OOB_REQ_EVT; + + int_evt = SMP_TK_REQ_EVT; + } + else if (model == SMP_MODEL_PASSKEY) + { + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + SMP_TRACE_EVENT1 ("p_cb->sec_level =%d (SMP_SEC_AUTHENTICATED) ", p_cb->sec_level ); + + p_cb->cb_evt = SMP_PASSKEY_REQ_EVT; + int_evt = SMP_TK_REQ_EVT; + } + else if (model == SMP_MODEL_KEY_NOTIF) + { + p_cb->sec_level = SMP_SEC_AUTHENTICATED; + + SMP_TRACE_DEBUG0("Need to generate Passkey"); + /* generate passkey and notify application */ + smp_generate_passkey(p_cb, NULL); + } + else if (model == SMP_MODEL_ENC_ONLY) /* TK = 0, go calculate Confirm */ + { + if (p_cb->role == HCI_ROLE_MASTER && + ((p_cb->peer_auth_req & SMP_AUTH_YN_BIT) != 0) && + ((p_cb->loc_auth_req & SMP_AUTH_YN_BIT) == 0)) + { + SMP_TRACE_ERROR0("IO capability does not meet authentication requirement"); + failure = SMP_PAIR_AUTH_FAIL; + p = (tSMP_INT_DATA *)&failure; + int_evt = SMP_AUTH_CMPL_EVT; + } + else + { + p_cb->sec_level = SMP_SEC_UNAUTHENTICATE; + SMP_TRACE_EVENT1 ("p_cb->sec_level =%d (SMP_SEC_UNAUTHENTICATE) ", p_cb->sec_level ); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + p = (tSMP_INT_DATA *)&key; + + memset(p_cb->tk, 0, BT_OCTET16_LEN); + /* TK, ready */ + int_evt = SMP_KEY_READY_EVT; + } + } + else if (model == SMP_MODEL_MAX) + { + SMP_TRACE_ERROR0("Association Model = SMP_MODEL_MAX (failed)"); + p = (tSMP_INT_DATA *)&failure; + int_evt = SMP_AUTH_CMPL_EVT; + } + + SMP_TRACE_EVENT1 ("sec_level=%d ", p_cb->sec_level ); + if (int_evt) + smp_sm_event(p_cb, int_evt, p); +} + +/******************************************************************************* +** Function smp_proc_io_rsp +** Description process IO response for a slave device. +*******************************************************************************/ +void smp_proc_io_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_proc_io_rsp "); + if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) + { + smp_set_state(SMP_ST_SEC_REQ_PENDING); + smp_send_cmd(SMP_OPCODE_SEC_REQ, p_cb); + } + else /* respond to pairing request */ + { + smp_send_pair_rsp(p_cb, NULL); + } +} +/******************************************************************************* +** Function smp_pairing_cmpl +** Description This function is called to send the pairing complete callback +** and remove the connection if needed. +*******************************************************************************/ +void smp_pairing_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + + SMP_TRACE_DEBUG0 ("smp_pairing_cmpl "); + + if ((p_cb->status == SMP_SUCCESS) || + (p_cb->status <= SMP_REPEATED_ATTEMPTS && p_cb->status != SMP_SUCCESS)) + { + smp_sm_event(p_cb, SMP_RELEASE_DELAY_EVT, p_data); + } + else + { + /* this will transition to idle state right away */ + smp_sm_event(p_cb, SMP_RELEASE_DELAY_TOUT_EVT, p_data); + } + +} +/******************************************************************************* +** Function smp_pair_terminate +** Description This function is called to send the pairing complete callback +** and remove the connection if needed. +*******************************************************************************/ +void smp_pair_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_pair_terminate "); + p_cb->status = SMP_PAIR_FAIL_UNKNOWN; + smp_proc_pairing_cmpl(p_cb); +} +/******************************************************************************* +** Function smp_idle_terminate +** Description This function calledin idle state to determine to send authentication +** complete or not. +*******************************************************************************/ +void smp_idle_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + if (p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) + { + SMP_TRACE_DEBUG0("Pairing terminated at IDLE state."); + p_cb->status = SMP_FAIL; + smp_proc_pairing_cmpl(p_cb); + } +} +/******************************************************************************* +** +** Function smp_link_encrypted +** +** Description This function is called when link is encrypted and notified to +** slave device. Proceed to to send LTK, DIV and ER to master if +** bonding the devices. +** +** +** Returns void +** +*******************************************************************************/ +void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable) +{ + tSMP_CB *p_cb = &smp_cb; + + SMP_TRACE_DEBUG1 ("smp_link_encrypted encr_enable=%d",encr_enable); + + if (memcmp(&smp_cb.pairing_bda[0], bda, BD_ADDR_LEN) == 0) + { + /* encryption completed with STK, remmeber the key size now, could be overwite + * when key exchange happens */ + if (p_cb->loc_enc_size != 0 && encr_enable) + { + /* update the link encryption key size if a SMP pairing just performed */ + btm_ble_update_sec_key_size(bda, p_cb->loc_enc_size); + } + + smp_sm_event(&smp_cb, SMP_ENCRYPTED_EVT, &encr_enable); + } +} +/******************************************************************************* +** +** Function smp_proc_ltk_request +** +** Description This function is called when LTK request is received from +** controller. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN smp_proc_ltk_request(BD_ADDR bda) +{ + SMP_TRACE_DEBUG1 ("smp_proc_ltk_request state = %d", smp_cb.state); + if ( smp_cb.state == SMP_ST_ENC_PENDING && + !memcmp(bda, smp_cb.pairing_bda, BD_ADDR_LEN)) + { + smp_sm_event(&smp_cb, SMP_ENC_REQ_EVT, NULL); + + return TRUE; + } + + return FALSE; +} +#endif + diff --git a/stack/smp/smp_api.c b/stack/smp/smp_api.c new file mode 100644 index 0000000..cfde2d4 --- /dev/null +++ b/stack/smp/smp_api.c @@ -0,0 +1,337 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the implementation of the SMP interface used by + * applications that can run over an SMP. + * + ******************************************************************************/ +#include + +#include "bt_target.h" +#if SMP_INCLUDED == TRUE + #include "smp_int.h" + #include "smp_api.h" + #include "l2cdefs.h" + #include "l2c_int.h" + #include "btm_int.h" + #include "hcimsgs.h" + + #include "btu.h" + + +/******************************************************************************* +** +** Function SMP_Init +** +** Description This function initializes the SMP unit. +** +** Returns void +** +*******************************************************************************/ +void SMP_Init(void) +{ + + SMP_TRACE_EVENT0 ("SMP_Init"); + memset(&smp_cb, 0, sizeof(tSMP_CB)); + +#if defined(SMP_INITIAL_TRACE_LEVEL) + smp_cb.trace_level = SMP_INITIAL_TRACE_LEVEL; +#else + smp_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + + smp_l2cap_if_init(); +} + + +/******************************************************************************* +** +** Function SMP_SetTraceLevel +** +** Description This function sets the trace level for SMP. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Input Parameters: +** level: The level to set the GATT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new or current trace level +** +*******************************************************************************/ +SMP_API extern UINT8 SMP_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) + smp_cb.trace_level = new_level; + + return(smp_cb.trace_level); +} + + +/******************************************************************************* +** +** Function SMP_Register +** +** Description This function register for the SMP services callback. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN SMP_Register (tSMP_CALLBACK *p_cback) +{ + SMP_TRACE_EVENT1 ("SMP_Register state=%d", smp_cb.state); + + if (smp_cb.p_callback != NULL) + { + SMP_TRACE_ERROR0 ("SMP_Register: duplicate registration, overwrite it"); + } + smp_cb.p_callback = p_cback; + + return(TRUE); + +} + +/******************************************************************************* +** +** Function SMP_Pair +** +** Description This function call to perform a SMP pairing with peer device. +** Device support one SMP pairing at one time. +** +** Parameters bd_addr - peer device bd address. +** +** Returns None +** +*******************************************************************************/ +tSMP_STATUS SMP_Pair (BD_ADDR bd_addr) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 status = SMP_PAIR_INTERNAL_ERR; + + BTM_TRACE_EVENT2 ("SMP_Pair state=%d flag=0x%x ", p_cb->state, p_cb->flags); + if (p_cb->state != SMP_ST_IDLE || p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) + { + /* pending security on going, reject this one */ + return SMP_BUSY; + } + else + { + p_cb->flags = SMP_PAIR_FLAGS_WE_STARTED_DD; + + memcpy (p_cb->pairing_bda, bd_addr, BD_ADDR_LEN); + + if (!L2CA_ConnectFixedChnl (L2CAP_SMP_CID, bd_addr)) + { + SMP_TRACE_ERROR0("SMP_Pair: L2C connect fixed channel failed."); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + return status; + } + + return SMP_STARTED; + } +} + + +/******************************************************************************* +** +** Function SMP_PairCancel +** +** Description This function call to cancel a SMP pairing with peer device. +** +** Parameters bd_addr - peer device bd address. +** +** Returns TRUE - Pairining is cancelled +** +*******************************************************************************/ +BOOLEAN SMP_PairCancel (BD_ADDR bd_addr) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 err_code = SMP_PAIR_FAIL_UNKNOWN; + BOOLEAN status = FALSE; + + BTM_TRACE_EVENT2 ("SMP_CancelPair state=%d flag=0x%x ", p_cb->state, p_cb->flags); + if ( (p_cb->state != SMP_ST_IDLE) && + (!memcmp (p_cb->pairing_bda, bd_addr, BD_ADDR_LEN)) ) + { + p_cb->is_pair_cancel = TRUE; + SMP_TRACE_DEBUG0("Cancel Pairing: set fail reason Unknown"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &err_code); + status = TRUE; + } + + return status; +} +/******************************************************************************* +** +** Function SMP_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation SMP_SUCCESS if success. +** Otherwise, SMP_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +void SMP_SecurityGrant(BD_ADDR bd_addr, UINT8 res) +{ + SMP_TRACE_EVENT0 ("SMP_SecurityGrant "); + if (smp_cb.state != SMP_ST_WAIT_APP_RSP || + smp_cb.cb_evt != SMP_SEC_REQUEST_EVT || + memcmp (smp_cb.pairing_bda, bd_addr, BD_ADDR_LEN)) + return; + + smp_sm_event(&smp_cb, SMP_API_SEC_GRANT_EVT, &res); +} + +/******************************************************************************* +** +** Function SMP_PasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation SMP_SUCCESS if success +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +void SMP_PasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey) +{ + tSMP_CB *p_cb = & smp_cb; + UINT8 failure = SMP_PASSKEY_ENTRY_FAIL; + tBTM_SEC_DEV_REC *p_dev_rec; + + SMP_TRACE_EVENT2 ("SMP_PasskeyReply: Key: %d Result:%d", + passkey, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (p_cb->cb_evt != SMP_PASSKEY_REQ_EVT) + { + SMP_TRACE_WARNING1 ("SMP_PasskeyReply() - Wrong State: %d", p_cb->state); + return; + } + + if (memcmp (bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) != 0) + { + SMP_TRACE_ERROR0 ("SMP_PasskeyReply() - Wrong BD Addr"); + return; + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + SMP_TRACE_ERROR0 ("SMP_PasskeyReply() - no dev CB"); + return; + } + + + if (passkey > BTM_MAX_PASSKEY_VAL || res != SMP_SUCCESS) + { + SMP_TRACE_WARNING1 ("SMP_PasskeyReply() - Wrong key len: %d or passkey entry fail", passkey); + /* send pairing failure */ + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + + } + else + { + smp_convert_string_to_tk(p_cb->tk, passkey); + } + + return; +} + +/******************************************************************************* +** +** Function SMP_OobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to SMP_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +void SMP_OobDataReply(BD_ADDR bd_addr, tSMP_STATUS res, UINT8 len, UINT8 *p_data) +{ + tSMP_CB *p_cb = & smp_cb; + UINT8 failure = SMP_OOB_FAIL; + tSMP_KEY key; + + SMP_TRACE_EVENT2 ("SMP_OobDataReply State: %d res:%d", + smp_cb.state, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (p_cb->state != SMP_ST_WAIT_APP_RSP || p_cb->cb_evt != SMP_OOB_REQ_EVT) + return; + + if (res != SMP_SUCCESS || len == 0 || !p_data) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } + else + { + if (len > BT_OCTET16_LEN) + len = BT_OCTET16_LEN; + + memcpy(p_cb->tk, p_data, len); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &key); + } +} + +/******************************************************************************* +** +** Function SMP_Encrypt +** +** Description This function is called to encrypt the data with the specified +** key +** +** Parameters: key - Pointer to key key[0] conatins the MSB +** key_len - key length +** plain_text - Pointer to data to be encrypted +** plain_text[0] conatins the MSB +** pt_len - plain text length +** p_out - output of the encrypted texts +** +** Returns Boolean - request is successful +*******************************************************************************/ +BOOLEAN SMP_Encrypt (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out) + +{ + BOOLEAN status=FALSE; + status = smp_encrypt_data(key, key_len, plain_text, pt_len, p_out); + return status; +} +#endif /* SMP_INCLUDED */ + + diff --git a/stack/smp/smp_cmac.c b/stack/smp/smp_cmac.c new file mode 100644 index 0000000..44e776e --- /dev/null +++ b/stack/smp/smp_cmac.c @@ -0,0 +1,388 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the implementation of the AES128 CMAC algorithm. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + #include + #include + + #include "btm_ble_api.h" + #include "smp_int.h" + #include "hcimsgs.h" + +typedef struct +{ + UINT8 *text; + UINT16 len; + UINT16 round; +}tCMAC_CB; + +tCMAC_CB cmac_cb; + +/* Rb for AES-128 as block cipher, LSB as [0] */ +BT_OCTET16 const_Rb = { + 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void print128(BT_OCTET16 x, const UINT8 *key_name) +{ +#if SMP_DEBUG == TRUE + UINT8 *p = (UINT8 *)x; + UINT8 i; + + SMP_TRACE_WARNING1("%s(MSB ~ LSB) = ", key_name); + + for (i = 0; i < 4; i ++) + { + SMP_TRACE_WARNING4("%02x %02x %02x %02x", + p[BT_OCTET16_LEN - i*4 -1], p[BT_OCTET16_LEN - i*4 -2], + p[BT_OCTET16_LEN - i*4 -3], p[BT_OCTET16_LEN - i*4 -4]); + } +#endif +} + +/******************************************************************************* +** +** Function padding +** +** Description utility function to padding the given text to be a 128 bits +** data. The parameter dest is input and output parameter, it +** must point to a BT_OCTET16_LEN memory space; where include +** length bytes valid data. +** +** Returns void +** +*******************************************************************************/ +static void padding ( BT_OCTET16 dest, UINT8 length ) +{ + UINT8 i, *p = dest; + /* original last block */ + for ( i = length ; i < BT_OCTET16_LEN; i++ ) + p[BT_OCTET16_LEN - i - 1] = ( i == length ) ? 0x80 : 0; +} +/******************************************************************************* +** +** Function leftshift_onebit +** +** Description utility function to left shift one bit for a 128 bits value. +** +** Returns void +** +*******************************************************************************/ +static void leftshift_onebit(UINT8 *input, UINT8 *output) +{ + UINT8 i, overflow = 0 , next_overflow = 0; + SMP_TRACE_EVENT0 ("leftshift_onebit "); + /* input[0] is LSB */ + for ( i = 0; i < BT_OCTET16_LEN ; i ++ ) + { + next_overflow = (input[i] & 0x80) ? 1:0; + output[i] = (input[i] << 1) | overflow; + overflow = next_overflow; + } + return; +} +/******************************************************************************* +** +** Function cmac_aes_cleanup +** +** Description clean up function for AES_CMAC algorithm. +** +** Returns void +** +*******************************************************************************/ +static void cmac_aes_cleanup(void) +{ + if (cmac_cb.text != NULL) + { + GKI_freebuf(cmac_cb.text); + } + memset(&cmac_cb, 0, sizeof(tCMAC_CB)); +} + +/******************************************************************************* +** +** Function cmac_aes_k_calculate +** +** Description This function is the calculation of block cipher using AES-128. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN cmac_aes_k_calculate(BT_OCTET16 key, UINT8 *p_signature, UINT16 tlen) +{ + tSMP_ENC output; + UINT8 i = 1, err = 0; + UINT8 x[16] = {0}; + UINT8 *p_mac; + + SMP_TRACE_EVENT0 ("cmac_aes_k_calculate "); + + while (i <= cmac_cb.round) + { + smp_xor_128(&cmac_cb.text[(cmac_cb.round - i)*BT_OCTET16_LEN], x); /* Mi' := Mi (+) X */ + + if (!SMP_Encrypt(key, BT_OCTET16_LEN, &cmac_cb.text[(cmac_cb.round - i)*BT_OCTET16_LEN], BT_OCTET16_LEN, &output)) + { + err = 1; + break; + } + + memcpy(x, output.param_buf, BT_OCTET16_LEN); + i ++; + } + + if (!err) + { + p_mac = output.param_buf + (BT_OCTET16_LEN - tlen); + memcpy(p_signature, p_mac, tlen); + + SMP_TRACE_DEBUG2("tlen = %d p_mac = %d", tlen, p_mac); + SMP_TRACE_DEBUG4("p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = 0x%02x", + *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); + SMP_TRACE_DEBUG4("p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = 0x%02x", + *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); + + return TRUE; + + } + else + return FALSE; +} +/******************************************************************************* +** +** Function cmac_prepare_last_block +** +** Description This function proceeed to prepare the last block of message +** Mn depending on the size of the message. +** +** Returns void +** +*******************************************************************************/ +static void cmac_prepare_last_block (BT_OCTET16 k1, BT_OCTET16 k2) +{ +// UINT8 x[16] = {0}; + BOOLEAN flag; + + SMP_TRACE_EVENT0 ("cmac_prepare_last_block "); + /* last block is a complete block set flag to 1 */ + flag = ((cmac_cb.len % BT_OCTET16_LEN) == 0 && cmac_cb.len != 0) ? TRUE : FALSE; + + SMP_TRACE_WARNING2("flag = %d round = %d", flag, cmac_cb.round); + + if ( flag ) + { /* last block is complete block */ + smp_xor_128(&cmac_cb.text[0], k1); + } + else /* padding then xor with k2 */ + { + padding(&cmac_cb.text[0], (UINT8)(cmac_cb.len % 16)); + + smp_xor_128(&cmac_cb.text[0], k2); + } +} +/******************************************************************************* +** +** Function cmac_subkey_cont +** +** Description This is the callback function when CIPHk(0[128]) is completed. +** +** Returns void +** +*******************************************************************************/ +static void cmac_subkey_cont(tSMP_ENC *p) +{ + UINT8 k1[BT_OCTET16_LEN], k2[BT_OCTET16_LEN]; + UINT8 *pp = p->param_buf; + SMP_TRACE_EVENT0 ("cmac_subkey_cont "); + print128(pp, (const UINT8 *)"K1 before shift"); + + /* If MSB(L) = 0, then K1 = L << 1 */ + if ( (pp[BT_OCTET16_LEN - 1] & 0x80) != 0 ) + { + /* Else K1 = ( L << 1 ) (+) Rb */ + leftshift_onebit(pp, k1); + smp_xor_128(k1, const_Rb); + } + else + { + leftshift_onebit(pp, k1); + } + + if ( (k1[BT_OCTET16_LEN - 1] & 0x80) != 0 ) + { + /* K2 = (K1 << 1) (+) Rb */ + leftshift_onebit(k1, k2); + smp_xor_128(k2, const_Rb); + } + else + { + /* If MSB(K1) = 0, then K2 = K1 << 1 */ + leftshift_onebit(k1, k2); + } + + print128(k1, (const UINT8 *)"K1"); + print128(k2, (const UINT8 *)"K2"); + + cmac_prepare_last_block (k1, k2); +} +/******************************************************************************* +** +** Function cmac_generate_subkey +** +** Description This is the function to generate the two subkeys. +** +** Parameters key - CMAC key, expect SRK when used by SMP. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN cmac_generate_subkey(BT_OCTET16 key) +{ + BT_OCTET16 z = {0}; + BOOLEAN ret = TRUE; + tSMP_ENC output; + SMP_TRACE_EVENT0 (" cmac_generate_subkey"); + + if (SMP_Encrypt(key, BT_OCTET16_LEN, z, BT_OCTET16_LEN, &output)) + { + cmac_subkey_cont(&output);; + } + else + ret = FALSE; + + return ret; +} +/******************************************************************************* +** +** Function AES_CMAC +** +** Description This is the AES-CMAC Generation Function with tlen implemented. +** +** Parameters key - CMAC key in little endian order, expect SRK when used by SMP. +** input - text to be signed in little endian byte order. +** length - length of the input in byte. +** tlen - lenth of mac desired +** p_signature - data pointer to where signed data to be stored, tlen long. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN AES_CMAC ( BT_OCTET16 key, UINT8 *input, UINT16 length, + UINT16 tlen, UINT8 *p_signature) +{ + UINT16 len, diff; + UINT16 n = (length + BT_OCTET16_LEN - 1) / BT_OCTET16_LEN; /* n is number of rounds */ + BOOLEAN ret = FALSE; + + SMP_TRACE_EVENT0 ("AES_CMAC "); + + if (n == 0) n = 1; + len = n * BT_OCTET16_LEN; + + SMP_TRACE_WARNING1("AES128_CMAC started, allocate buffer size = %d", len); + /* allocate a memory space of multiple of 16 bytes to hold text */ + if ((cmac_cb.text = (UINT8 *)GKI_getbuf(len)) != NULL) + { + cmac_cb.round = n; + + memset(cmac_cb.text, 0, len); + diff = len - length; + + if (input != NULL && length > 0) + { + memcpy(&cmac_cb.text[diff] , input, (int)length); + cmac_cb.len = length; + } + else + cmac_cb.len = 0; + + /* prepare calculation for subkey s and last block of data */ + if (cmac_generate_subkey(key)) + { + /* start calculation */ + ret = cmac_aes_k_calculate(key, p_signature, tlen); + } + /* clean up */ + cmac_aes_cleanup(); + } + else + { + ret = FALSE; + SMP_TRACE_ERROR0("No resources"); + } + + return ret; +} + + #if 0 /* testing code, sample data from spec */ +void test_cmac_cback(UINT8 *p_mac, UINT16 tlen) +{ + SMP_TRACE_EVENT0 ("test_cmac_cback "); + SMP_TRACE_ERROR0("test_cmac_cback"); +} + +void test_cmac(void) +{ + SMP_TRACE_EVENT0 ("test_cmac "); + UINT8 M[64] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 + }; + + UINT8 key[16] = { + 0x3c, 0x4f, 0xcf, 0x09, 0x88, 0x15, 0xf7, 0xab, + 0xa6, 0xd2, 0xae, 0x28, 0x16, 0x15, 0x7e, 0x2b + }; + UINT8 i =0, tmp; + UINT16 len; + + len = 64; + + for (i = 0; i < len/2; i ++) + { + tmp = M[i]; + M[i] = M[len -1 - i]; + M[len -1 - i] = tmp; + } + + + memset(&cmac_cb, 0, sizeof(tCMAC_CB)); + + SMP_TRACE_WARNING1("\n Example 1: len = %d\n", len); + + AES_CMAC(key, M, len, 128, test_cmac_cback, 0); + +} + #endif +#endif + diff --git a/stack/smp/smp_int.h b/stack/smp/smp_int.h new file mode 100644 index 0000000..9eb2f10 --- /dev/null +++ b/stack/smp/smp_int.h @@ -0,0 +1,315 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains internally used SMP definitions + * + ******************************************************************************/ + +#ifndef SMP_INT_H +#define SMP_INT_H + +#include "btu.h" +#include "smp_api.h" + +#define SMP_MODEL_ENC_ONLY 0 +#define SMP_MODEL_PASSKEY 1 +#define SMP_MODEL_OOB 2 +#define SMP_MODEL_KEY_NOTIF 3 +#define SMP_MODEL_MAX 4 +typedef UINT8 tSMP_ASSO_MODEL; + + +#ifndef SMP_MAX_CONN + #define SMP_MAX_CONN 2 +#endif + +#define SMP_WAIT_FOR_RSP_TOUT 30 +#define SMP_WAIT_FOR_REL_DELAY_TOUT 5 +/* SMP L2CAP command code */ +#define SMP_OPCODE_PAIRING_REQ 0x01 +#define SMP_OPCODE_PAIRING_RSP 0x02 +#define SMP_OPCODE_CONFIRM 0x03 +#define SMP_OPCODE_INIT 0x04 +#define SMP_OPCODE_PAIRING_FAILED 0x05 +#define SMP_OPCODE_ENCRYPT_INFO 0x06 +#define SMP_OPCODE_MASTER_ID 0x07 +#define SMP_OPCODE_IDENTITY_INFO 0x08 +#define SMP_OPCODE_ID_ADDR 0x09 +#define SMP_OPCODE_SIGN_INFO 0x0A +#define SMP_OPCODE_SEC_REQ 0x0B +#define SMP_OPCODE_MAX (SMP_OPCODE_SEC_REQ + 1) + +/* SMP events */ +#define SMP_PAIRING_REQ_EVT SMP_OPCODE_PAIRING_REQ +#define SMP_PAIRING_RSP_EVT SMP_OPCODE_PAIRING_RSP +#define SMP_CONFIRM_EVT SMP_OPCODE_CONFIRM +#define SMP_RAND_EVT SMP_OPCODE_INIT +#define SMP_PAIRING_FAILED_EVT SMP_OPCODE_PAIRING_FAILED +#define SMP_ENCRPTION_INFO_EVT SMP_OPCODE_ENCRYPT_INFO +#define SMP_MASTER_ID_EVT SMP_OPCODE_MASTER_ID +#define SMP_ID_INFO_EVT SMP_OPCODE_IDENTITY_INFO +#define SMP_ID_ADDR_EVT SMP_OPCODE_ID_ADDR +#define SMP_SIGN_INFO_EVT SMP_OPCODE_SIGN_INFO +#define SMP_SECURITY_REQ_EVT SMP_OPCODE_SEC_REQ + +#define SMP_SELF_DEF_EVT SMP_SECURITY_REQ_EVT +#define SMP_KEY_READY_EVT (SMP_SELF_DEF_EVT + 1) +#define SMP_ENCRYPTED_EVT (SMP_SELF_DEF_EVT + 2) +#define SMP_L2CAP_CONN_EVT (SMP_SELF_DEF_EVT + 3) +#define SMP_L2CAP_DISCONN_EVT (SMP_SELF_DEF_EVT + 4) +#define SMP_IO_RSP_EVT (SMP_SELF_DEF_EVT + 5) +#define SMP_API_SEC_GRANT_EVT (SMP_SELF_DEF_EVT + 6) +#define SMP_TK_REQ_EVT (SMP_SELF_DEF_EVT + 7) +#define SMP_AUTH_CMPL_EVT (SMP_SELF_DEF_EVT + 8) +#define SMP_ENC_REQ_EVT (SMP_SELF_DEF_EVT + 9) +#define SMP_BOND_REQ_EVT (SMP_SELF_DEF_EVT + 10) +#define SMP_DISCARD_SEC_REQ_EVT (SMP_SELF_DEF_EVT + 11) +#define SMP_RELEASE_DELAY_EVT (SMP_SELF_DEF_EVT + 12) +#define SMP_RELEASE_DELAY_TOUT_EVT (SMP_SELF_DEF_EVT + 13) +typedef UINT8 tSMP_EVENT; +#define SMP_MAX_EVT SMP_RELEASE_DELAY_TOUT_EVT + 1 + +/* auumption it's only using the low 8 bits, if bigger than that, need to expand it to be 16 bits */ +#define SMP_SEC_KEY_MASK 0x00ff + +/* SMP pairing state */ +enum +{ + SMP_ST_IDLE, + SMP_ST_WAIT_APP_RSP, + SMP_ST_SEC_REQ_PENDING, + SMP_ST_PAIR_REQ_RSP, + SMP_ST_WAIT_CONFIRM, + SMP_ST_CONFIRM, + SMP_ST_RAND, + SMP_ST_ENC_PENDING, + SMP_ST_BOND_PENDING, + SMP_ST_RELEASE_DELAY, + SMP_ST_MAX +}; +typedef UINT8 tSMP_STATE; + +/* random and encrption activity state */ +enum +{ + SMP_GEN_COMPARE = 1, + SMP_GEN_CONFIRM, + + SMP_GEN_DIV_LTK, + SMP_GEN_DIV_CSRK, + SMP_GEN_RAND_V, + SMP_GEN_TK, + SMP_GEN_SRAND_MRAND, + SMP_GEN_SRAND_MRAND_CONT +}; + +enum +{ + SMP_KEY_TYPE_TK, + SMP_KEY_TYPE_CFM, + SMP_KEY_TYPE_CMP, + SMP_KEY_TYPE_STK, + SMP_KEY_TYPE_LTK +}; +typedef struct +{ + UINT8 key_type; + UINT8* p_data; +}tSMP_KEY; + +typedef union +{ + UINT8 *p_data; /* UINT8 type data pointer */ + tSMP_KEY key; + UINT16 reason; +}tSMP_INT_DATA; + +/* internal status mask */ +#define SMP_PAIR_FLAGS_WE_STARTED_DD (1) +#define SMP_PAIR_FLAGS_PEER_STARTED_DD (1 << 1) +#define SMP_PAIR_FLAGS_CMD_CONFIRM (1 << SMP_OPCODE_CONFIRM) /* 1 << 3 */ +#define SMP_PAIR_FLAG_ENC_AFTER_PAIR (1 << 4) + +/* check if authentication requirement need MITM protection */ +#define SMP_NO_MITM_REQUIRED(x) (((x) & SMP_AUTH_YN_BIT) == 0) + +#define SMP_ENCRYT_KEY_SIZE 16 +#define SMP_ENCRYT_DATA_SIZE 16 +#define SMP_ECNCRPYT_STATUS HCI_SUCCESS + +/* SMP control block */ +typedef struct +{ + tSMP_CALLBACK *p_callback; + TIMER_LIST_ENT rsp_timer_ent; + UINT8 trace_level; + + BD_ADDR pairing_bda; + + tSMP_STATE state; + UINT8 failure; + UINT8 status; + UINT8 role; + UINT8 flags; + UINT8 cb_evt; + + tSMP_SEC_LEVEL sec_level; + BOOLEAN connect_initialized; + BT_OCTET16 confirm; + BT_OCTET16 rconfirm; + BT_OCTET16 rrand; + BT_OCTET16 rand; + tSMP_IO_CAP peer_io_caps; + tSMP_IO_CAP loc_io_caps; + tSMP_OOB_FLAG peer_oob_flag; + tSMP_OOB_FLAG loc_oob_flag; + tSMP_AUTH_REQ peer_auth_req; + tSMP_AUTH_REQ loc_auth_req; + UINT8 peer_enc_size; + UINT8 loc_enc_size; + UINT8 peer_i_key; + UINT8 peer_r_key; + UINT8 loc_i_key; + UINT8 loc_r_key; + + BT_OCTET16 tk; + BT_OCTET16 ltk; + UINT16 div; + BT_OCTET16 csrk; /* storage for local CSRK */ + UINT16 ediv; + BT_OCTET8 enc_rand; + + UINT8 rand_enc_proc; + BOOLEAN last_cmd; + BD_ADDR local_bda; + BOOLEAN is_pair_cancel; + BOOLEAN discard_sec_req; +#if SMP_CONFORMANCE_TESTING == TRUE + BOOLEAN enable_test_confirm_val; + BT_OCTET16 test_confirm; + BOOLEAN enable_test_rand_val; + BT_OCTET16 test_rand; + BOOLEAN enable_test_pair_fail; + UINT8 pair_fail_status; + BOOLEAN remove_fixed_channel_disable; +#endif + +}tSMP_CB; + +/* Server Action functions are of this type */ +typedef void (*tSMP_ACT)(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if SMP_DYNAMIC_MEMORY == FALSE + SMP_API extern tSMP_CB smp_cb; +#else + SMP_API extern tSMP_CB *smp_cb_ptr; +#define smp_cb (*smp_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif + +/* Functions provided by att_main.c */ +SMP_API extern void smp_init (void); + +#if SMP_CONFORMANCE_TESTING == TRUE +/* Used only for conformance testing */ +SMP_API extern void smp_set_test_confirm_value (BOOLEAN enable, UINT8 *p_c_value); +SMP_API extern void smp_set_test_rand_value (BOOLEAN enable, UINT8 *p_c_value); +SMP_API extern void smp_set_test_pair_fail_status (BOOLEAN enable, UINT8 status); +SMP_API extern void smp_remove_fixed_channel_disable (BOOLEAN disable); +#endif +/* smp main */ +extern void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data); + +extern void smp_proc_sec_request(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sec_request(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_pair_fail(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_init(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_master_id(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_id_addr(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sec_grant(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sec_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_sl_key(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_start_enc(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_enc_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_discard(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_release_delay(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_release_delay_tout(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_pairing_cmpl(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_decide_asso_model(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_app_cback(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_compare(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_check_auth_req(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_io_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_id_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_enc_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_csrk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_ltk_reply(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_pair_cmd(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_pair_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_idle_terminate(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_send_pair_rsp(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_key_distribution(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_proc_srk_info(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +/* smp_l2c */ +extern void smp_l2cap_if_init (void); + +/* smp utility */ +extern BOOLEAN smp_send_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +extern void smp_cb_cleanup(tSMP_CB *p_cb); +extern void smp_reset_control_value(tSMP_CB *p_cb); +extern void smp_proc_pairing_cmpl(tSMP_CB *p_cb); +extern void smp_convert_string_to_tk(BT_OCTET16 tk, UINT32 passkey); +extern void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 * p_data); +extern void smp_rsp_timeout(TIMER_LIST_ENT *p_tle); +extern void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b); +extern BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out); +/* smp key */ +extern void smp_generate_confirm (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_compare (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_stk (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_ltk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_generate_passkey (tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +extern void smp_genenrate_rand_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); + +/* smp main util */ +extern void smp_set_state(tSMP_STATE state); +extern tSMP_STATE smp_get_state(void); + +#endif /* SMP_INT_H */ + diff --git a/stack/smp/smp_keys.c b/stack/smp/smp_keys.c new file mode 100644 index 0000000..c61b548 --- /dev/null +++ b/stack/smp/smp_keys.c @@ -0,0 +1,909 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the implementation of the SMP utility functions used + * by SMP. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + #if SMP_DEBUG == TRUE + #include + #endif + #include + + #include "btm_ble_api.h" + #include "smp_int.h" + #include "btm_int.h" + #include "btm_ble_int.h" + #include "hcimsgs.h" + #include "aes.h" + #ifndef SMP_MAX_ENC_REPEAT + #define SMP_MAX_ENC_REPEAT 3 + #endif + +static void smp_rand_back(tBTM_RAND_ENC *p); +static void smp_genenrate_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +static void smp_genenrate_ltk_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data); +static void smp_generate_y(tSMP_CB *p_cb, tSMP_INT_DATA *p); +static void smp_generate_rand_vector (tSMP_CB *p_cb, tSMP_INT_DATA *p); +static void smp_process_stk(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_calculate_comfirm_cont(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_confirm(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_compare(tSMP_CB *p_cb, tSMP_ENC *p); +static void smp_process_ediv(tSMP_CB *p_cb, tSMP_ENC *p); + +static const tSMP_ACT smp_encrypt_action[] = +{ + smp_generate_compare, /* SMP_GEN_COMPARE */ + smp_genenrate_confirm, /* SMP_GEN_CONFIRM*/ + smp_generate_stk, /* SMP_GEN_STK*/ + smp_genenrate_ltk_cont, /* SMP_GEN_LTK */ + smp_generate_ltk, /* SMP_GEN_DIV_LTK */ + smp_generate_rand_vector, /* SMP_GEN_RAND_V */ + smp_generate_y, /* SMP_GEN_EDIV */ + smp_generate_passkey, /* SMP_GEN_TK */ + smp_generate_confirm, /* SMP_GEN_SRAND_MRAND */ + smp_genenrate_rand_cont /* SMP_GEN_SRAND_MRAND_CONT */ +}; + + + #define SMP_PASSKEY_MASK 0xfff00000 + + #if SMP_DEBUG == TRUE +static void smp_debug_print_nbyte_little_endian (UINT8 *p, const UINT8 *key_name, UINT8 len) +{ + int i, x = 0; + UINT8 p_buf[100]; + memset(p_buf, 0, 100); + + for (i = 0; i < len; i ++) + { + x += sprintf ((char *)&p_buf[x], "%02x ", p[i]); + } + SMP_TRACE_WARNING2("%s(LSB ~ MSB) = %s", key_name, p_buf); +} + #else + #define smp_debug_print_nbyte_little_endian(p, key_name, len) + #endif + +/******************************************************************************* +** +** Function smp_encrypt_data +** +** Description This function is called to generate passkey. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN smp_encrypt_data (UINT8 *key, UINT8 key_len, + UINT8 *plain_text, UINT8 pt_len, + tSMP_ENC *p_out) +{ + aes_context ctx; + UINT8 *p_start = NULL; + UINT8 *p = NULL; + UINT8 *p_rev_data = NULL; /* input data in big endilan format */ + UINT8 *p_rev_key = NULL; /* input key in big endilan format */ + UINT8 *p_rev_output = NULL; /* encrypted output in big endilan format */ + + SMP_TRACE_DEBUG0 ("smp_encrypt_data"); + if ( (p_out == NULL ) || (key_len != SMP_ENCRYT_KEY_SIZE) ) + { + BTM_TRACE_ERROR0 ("smp_encrypt_data Failed"); + return(FALSE); + } + + if ((p_start = (UINT8 *)GKI_getbuf((SMP_ENCRYT_DATA_SIZE*4))) == NULL) + { + BTM_TRACE_ERROR0 ("smp_encrypt_data Failed unable to allocate buffer"); + return(FALSE); + } + + if (pt_len > SMP_ENCRYT_DATA_SIZE) + pt_len = SMP_ENCRYT_DATA_SIZE; + + memset(p_start, 0, SMP_ENCRYT_DATA_SIZE * 4); + p = p_start; + ARRAY_TO_STREAM (p, plain_text, pt_len); /* byte 0 to byte 15 */ + p_rev_data = p = p_start + SMP_ENCRYT_DATA_SIZE; /* start at byte 16 */ + REVERSE_ARRAY_TO_STREAM (p, p_start, SMP_ENCRYT_DATA_SIZE); /* byte 16 to byte 31 */ + p_rev_key = p; /* start at byte 32 */ + REVERSE_ARRAY_TO_STREAM (p, key, SMP_ENCRYT_KEY_SIZE); /* byte 32 to byte 47 */ + + smp_debug_print_nbyte_little_endian(key, (const UINT8 *)"Key", SMP_ENCRYT_KEY_SIZE); + smp_debug_print_nbyte_little_endian(p_start, (const UINT8 *)"Plain text", SMP_ENCRYT_DATA_SIZE); + p_rev_output = p; + aes_set_key(p_rev_key, SMP_ENCRYT_KEY_SIZE, &ctx); + aes_encrypt(p_rev_data, p, &ctx); /* outputs in byte 48 to byte 63 */ + + p = p_out->param_buf; + REVERSE_ARRAY_TO_STREAM (p, p_rev_output, SMP_ENCRYT_DATA_SIZE); + smp_debug_print_nbyte_little_endian(p_out->param_buf, (const UINT8 *)"Encrypted text", SMP_ENCRYT_KEY_SIZE); + + p_out->param_len = SMP_ENCRYT_KEY_SIZE; + p_out->status = HCI_SUCCESS; + p_out->opcode = HCI_BLE_ENCRYPT; + + GKI_freebuf(p_start); + + return(TRUE); +} + + +/******************************************************************************* +** +** Function smp_generate_passkey +** +** Description This function is called to generate passkey. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_passkey(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_generate_passkey"); + p_cb->rand_enc_proc = SMP_GEN_TK; + + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); +} +/******************************************************************************* +** +** Function smp_proc_passkey +** +** Description This function is called to process a passkey. +** +** Returns void +** +*******************************************************************************/ +void smp_proc_passkey(tSMP_CB *p_cb , tBTM_RAND_ENC *p) +{ + UINT8 *tt = p_cb->tk; + tSMP_KEY key; + UINT32 passkey; //= 19655 test number; */ + UINT8 *pp = p->param_buf; + + SMP_TRACE_DEBUG0 ("smp_proc_passkey "); + STREAM_TO_UINT32(passkey, pp); + passkey &= ~SMP_PASSKEY_MASK; + + /* truncate by maximum value */ + while (passkey > BTM_MAX_PASSKEY_VAL) + passkey >>= 1; + SMP_TRACE_ERROR1("Passkey generated = %d", passkey); + + /* save the TK */ + memset(p_cb->tk, 0, BT_OCTET16_LEN); + UINT32_TO_STREAM(tt, passkey); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = p_cb->tk; + + if (p_cb->p_callback) + { + (*p_cb->p_callback)(SMP_PASSKEY_NOTIF_EVT, p_cb->pairing_bda, (tSMP_EVT_DATA *)&passkey); + } + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, (tSMP_INT_DATA *)&key); +} + + +/******************************************************************************* +** +** Function smp_generate_stk +** +** Description This function is called to generate STK calculated by running +** AES with the TK value as key and a concatenation of the random +** values. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_stk (tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BT_OCTET16 ptext; + UINT8 *p = ptext; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG0 ("smp_generate_stk "); + + memset(p, 0, BT_OCTET16_LEN); + if (p_cb->role == HCI_ROLE_MASTER) + { + memcpy(p, p_cb->rand, BT_OCTET8_LEN); + memcpy(&p[BT_OCTET8_LEN], p_cb->rrand, BT_OCTET8_LEN); + } + else + { + memcpy(p, p_cb->rrand, BT_OCTET8_LEN); + memcpy(&p[BT_OCTET8_LEN], p_cb->rand, BT_OCTET8_LEN); + } + + /* generate STK = Etk(rand|rrand)*/ + if (!SMP_Encrypt( p_cb->tk, BT_OCTET16_LEN, ptext, BT_OCTET16_LEN, &output)) + { + SMP_TRACE_ERROR0("smp_generate_stk failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + smp_process_stk(p_cb, &output); + } + +} +/******************************************************************************* +** +** Function smp_generate_confirm +** +** Description This function is called to start the second pairing phase by +** start generating initializer random number. +** +** +** Returns void +** +*******************************************************************************/ +void smp_generate_confirm (tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_generate_confirm"); + p_cb->rand_enc_proc = SMP_GEN_SRAND_MRAND; + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); +} +/******************************************************************************* +** +** Function smp_genenrate_rand_cont +** +** Description This function is called to generate another 64 bits random for +** MRand or Srand. +** +** Returns void +** +*******************************************************************************/ +void smp_genenrate_rand_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_genenrate_rand_cont "); + p_cb->rand_enc_proc = SMP_GEN_SRAND_MRAND_CONT; + /* generate 64 MSB of MRand or SRand */ + + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); +} +/******************************************************************************* +** +** Function smp_generate_ltk +** +** Description This function is called to calculate LTK, starting with DIV +** generation. +** +** +** Returns void +** +*******************************************************************************/ +void smp_generate_ltk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BOOLEAN div_status; + + SMP_TRACE_DEBUG0 ("smp_generate_ltk "); + + div_status = btm_get_local_div(p_cb->pairing_bda, &p_cb->div); + + if (div_status) + { + smp_genenrate_ltk_cont(p_cb, NULL); + } + else + { + SMP_TRACE_DEBUG0 ("Generate DIV for LTK"); + p_cb->rand_enc_proc = SMP_GEN_DIV_LTK; + /* generate MRand or SRand */ + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); + } +} + + +/******************************************************************************* +** +** Function smp_compute_csrk +** +** Description This function is called to calculate CSRK +** +** +** Returns void +** +*******************************************************************************/ +void smp_compute_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BT_OCTET16 er; + UINT8 buffer[4]; /* for (r || DIV) r=1*/ + UINT16 r=1; + UINT8 *p=buffer; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG1 ("smp_compute_csrk div=%x", p_cb->div); + BTM_GetDeviceEncRoot(er); + /* CSRK = d1(ER, DIV, 1) */ + UINT16_TO_STREAM(p, p_cb->div); + UINT16_TO_STREAM(p, r); + + if (!SMP_Encrypt(er, BT_OCTET16_LEN, buffer, 4, &output)) + { + SMP_TRACE_ERROR0("smp_generate_csrk failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + memcpy((void *)p_cb->csrk, output.param_buf, BT_OCTET16_LEN); + smp_send_csrk_info(p_cb, NULL); + } +} + +/******************************************************************************* +** +** Function smp_generate_csrk +** +** Description This function is called to calculate LTK, starting with DIV +** generation. +** +** +** Returns void +** +*******************************************************************************/ +void smp_generate_csrk(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BOOLEAN div_status; + + SMP_TRACE_DEBUG0 ("smp_generate_csrk"); + + div_status = btm_get_local_div(p_cb->pairing_bda, &p_cb->div); + if (div_status) + { + smp_compute_csrk(p_cb, NULL); + } + else + { + SMP_TRACE_DEBUG0 ("Generate DIV for CSRK"); + p_cb->rand_enc_proc = SMP_GEN_DIV_CSRK; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); + } +} + + +/******************************************************************************* +** Function smp_concatenate_peer +** add pairing command sent from local device into p1. +*******************************************************************************/ +void smp_concatenate_local( tSMP_CB *p_cb, UINT8 **p_data, UINT8 op_code) +{ + UINT8 *p = *p_data; + + SMP_TRACE_DEBUG0 ("smp_concatenate_local "); + UINT8_TO_STREAM(p, op_code); + UINT8_TO_STREAM(p, p_cb->loc_io_caps); + UINT8_TO_STREAM(p, p_cb->loc_oob_flag); + UINT8_TO_STREAM(p, p_cb->loc_auth_req); + UINT8_TO_STREAM(p, p_cb->loc_enc_size); + UINT8_TO_STREAM(p, p_cb->loc_i_key); + UINT8_TO_STREAM(p, p_cb->loc_r_key); + + *p_data = p; +} +/******************************************************************************* +** Function smp_concatenate_peer +** add pairing command received from peer device into p1. +*******************************************************************************/ +void smp_concatenate_peer( tSMP_CB *p_cb, UINT8 **p_data, UINT8 op_code) +{ + UINT8 *p = *p_data; + + SMP_TRACE_DEBUG0 ("smp_concatenate_peer "); + UINT8_TO_STREAM(p, op_code); + UINT8_TO_STREAM(p, p_cb->peer_io_caps); + UINT8_TO_STREAM(p, p_cb->peer_oob_flag); + UINT8_TO_STREAM(p, p_cb->peer_auth_req); + UINT8_TO_STREAM(p, p_cb->peer_enc_size); + UINT8_TO_STREAM(p, p_cb->peer_i_key); + UINT8_TO_STREAM(p, p_cb->peer_r_key); + + *p_data = p; +} +/******************************************************************************* +** +** Function smp_gen_p1_4_confirm +** +** Description Generate Confirm/Compare Step1: +** p1 = pres || preq || rat' || iat' +** +** Returns void +** +*******************************************************************************/ +void smp_gen_p1_4_confirm( tSMP_CB *p_cb, BT_OCTET16 p1) +{ + UINT8 *p = (UINT8 *)p1; + tBTM_SEC_DEV_REC *p_dev_rec; + + SMP_TRACE_DEBUG0 ("smp_gen_p1_4_confirm"); + if ((p_dev_rec = btm_find_dev (p_cb->pairing_bda)) == NULL) + { + SMP_TRACE_ERROR0("can not generate confirm for unknown device"); + return; + } + + BTM_ReadConnectionAddr(p_cb->local_bda); + + if (p_cb->role == HCI_ROLE_MASTER) + { + /* LSB : rat': initiator's(local) address type */ + UINT8_TO_STREAM(p, 0); + /* LSB : iat': responder's address type */ + UINT8_TO_STREAM(p, p_dev_rec->ble.ble_addr_type); + /* concatinate preq */ + smp_concatenate_local(p_cb, &p, SMP_OPCODE_PAIRING_REQ); + /* concatinate pres */ + smp_concatenate_peer(p_cb, &p, SMP_OPCODE_PAIRING_RSP); + } + else + { + /* LSB : iat': initiator's address type */ + UINT8_TO_STREAM(p, p_dev_rec->ble.ble_addr_type); + /* LSB : rat': responder's(local) address type */ + UINT8_TO_STREAM(p, 0); + /* concatinate preq */ + smp_concatenate_peer(p_cb, &p, SMP_OPCODE_PAIRING_REQ); + /* concatinate pres */ + smp_concatenate_local(p_cb, &p, SMP_OPCODE_PAIRING_RSP); + } +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG0("p1 = pres || preq || rat' || iat'"); + smp_debug_print_nbyte_little_endian ((UINT8 *)p1, (const UINT8 *)"P1", 16); +#endif +} +/******************************************************************************* +** +** Function smp_gen_p2_4_confirm +** +** Description Generate Confirm/Compare Step2: +** p2 = padding || ia || ra +** +** Returns void +** +*******************************************************************************/ +void smp_gen_p2_4_confirm( tSMP_CB *p_cb, BT_OCTET16 p2) +{ + UINT8 *p = (UINT8 *)p2; + + SMP_TRACE_DEBUG0 ("smp_gen_p2_4_confirm"); + memset(p, 0, sizeof(BT_OCTET16)); + + if (p_cb->role == HCI_ROLE_MASTER) + { + /* LSB ra */ + BDADDR_TO_STREAM(p, p_cb->pairing_bda); + /* ia */ + BDADDR_TO_STREAM(p, p_cb->local_bda); + } + else + { + /* LSB ra */ + BDADDR_TO_STREAM(p, p_cb->local_bda); + /* ia */ + BDADDR_TO_STREAM(p, p_cb->pairing_bda); + } +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG0("p2 = padding || ia || ra"); + smp_debug_print_nbyte_little_endian(p2, (const UINT8 *)"p2", 16); +#endif +} +/******************************************************************************* +** +** Function smp_calculate_comfirm +** +** Description This function is called to calculate Confirm value. +** +** Returns void +** +*******************************************************************************/ +void smp_calculate_comfirm (tSMP_CB *p_cb, BT_OCTET16 rand, BD_ADDR bda) +{ + BT_OCTET16 p1; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG0 ("smp_calculate_comfirm "); + /* generate p1 = pres || preq || rat' || iat' */ + smp_gen_p1_4_confirm(p_cb, p1); + + /* p1 = rand XOR p1 */ + smp_xor_128(p1, rand); + + smp_debug_print_nbyte_little_endian ((UINT8 *)p1, (const UINT8 *)"P1' = r XOR p1", 16); + + /* calculate e(k, r XOR p1), where k = TK */ + if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p1, BT_OCTET16_LEN, &output)) + { + SMP_TRACE_ERROR0("smp_generate_csrk failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + smp_calculate_comfirm_cont(p_cb, &output); + } +} +/******************************************************************************* +** +** Function smp_calculate_comfirm_cont +** +** Description This function is called when SConfirm/MConfirm is generated +** proceed to send the Confirm request/response to peer device. +** +** Returns void +** +*******************************************************************************/ +static void smp_calculate_comfirm_cont(tSMP_CB *p_cb, tSMP_ENC *p) +{ + BT_OCTET16 p2; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG0 ("smp_calculate_comfirm_cont "); +#if SMP_DEBUG == TRUE + SMP_TRACE_DEBUG0("Confirm step 1 p1' = e(k, r XOR p1) Generated"); + smp_debug_print_nbyte_little_endian (p->param_buf, (const UINT8 *)"C1", 16); +#endif + + smp_gen_p2_4_confirm(p_cb, p2); + + /* calculate p2 = (p1' XOR p2) */ + smp_xor_128(p2, p->param_buf); + smp_debug_print_nbyte_little_endian ((UINT8 *)p2, (const UINT8 *)"p2' = C1 xor p2", 16); + + /* calculate: Confirm = E(k, p1' XOR p2) */ + if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p2, BT_OCTET16_LEN, &output)) + { + SMP_TRACE_ERROR0("smp_calculate_comfirm_cont failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + switch (p_cb->rand_enc_proc) + { + case SMP_GEN_CONFIRM: + smp_process_confirm(p_cb, &output); + break; + + case SMP_GEN_COMPARE: + smp_process_compare(p_cb, &output); + break; + } + } +} +/******************************************************************************* +** +** Function smp_genenrate_confirm +** +** Description This function is called when a 48 bits random number is generated +** as SRand or MRand, continue to calculate Sconfirm or MConfirm. +** +** Returns void +** +*******************************************************************************/ +static void smp_genenrate_confirm(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_genenrate_confirm "); + p_cb->rand_enc_proc = SMP_GEN_CONFIRM; + + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->rand, (const UINT8 *)"local rand", 16); + + smp_calculate_comfirm(p_cb, p_cb->rand, p_cb->pairing_bda); +} +/******************************************************************************* +** +** Function smp_generate_compare +** +** Description This function is called to generate SConfirm for Slave device, +** or MSlave for Master device. This function can be also used for +** generating Compare number for confirm value check. +** +** Returns void +** +*******************************************************************************/ +void smp_generate_compare (tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + SMP_TRACE_DEBUG0 ("smp_generate_compare "); + p_cb->rand_enc_proc = SMP_GEN_COMPARE; + + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->rrand, (const UINT8 *)"peer rand", 16); + + smp_calculate_comfirm(p_cb, p_cb->rrand, p_cb->local_bda); +} +/******************************************************************************* +** +** Function smp_process_confirm +** +** Description This function is called when SConfirm/MConfirm is generated +** proceed to send the Confirm request/response to peer device. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_confirm(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG0 ("smp_process_confirm "); +#if SMP_CONFORMANCE_TESTING == TRUE + if (p_cb->enable_test_confirm_val) + { + BTM_TRACE_DEBUG0 ("Use confirm value from script"); + memcpy(p_cb->confirm, p_cb->test_confirm, BT_OCTET16_LEN); + } + else + memcpy(p_cb->confirm, p->param_buf, BT_OCTET16_LEN); +#else + memcpy(p_cb->confirm, p->param_buf, BT_OCTET16_LEN); +#endif + + +#if (SMP_DEBUG == TRUE) + SMP_TRACE_DEBUG0("Confirm Generated"); + smp_debug_print_nbyte_little_endian ((UINT8 *)p_cb->confirm, (const UINT8 *)"Confirm", 16); +#endif + + key.key_type = SMP_KEY_TYPE_CFM; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} +/******************************************************************************* +** +** Function smp_process_compare +** +** Description This function is called when Compare is generated using the +** RRand and local BDA, TK information. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_compare(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG0 ("smp_process_compare "); +#if (SMP_DEBUG == TRUE) + SMP_TRACE_DEBUG0("Compare Generated"); + smp_debug_print_nbyte_little_endian (p->param_buf, (const UINT8 *)"Compare", 16); +#endif + key.key_type = SMP_KEY_TYPE_CMP; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_process_stk +** +** Description This function is called when STK is generated +** proceed to send the encrypt the link using STK. +** +** Returns void +** +*******************************************************************************/ +static void smp_process_stk(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + + SMP_TRACE_DEBUG0 ("smp_process_stk "); +#if (SMP_DEBUG == TRUE) + SMP_TRACE_ERROR0("STK Generated"); +#endif + smp_mask_enc_key(p_cb->loc_enc_size, p->param_buf); + + key.key_type = SMP_KEY_TYPE_STK; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_genenrate_ltk_cont +** +** Description This function is to calculate LTK = d1(ER, DIV, 0)= e(ER, DIV) +** +** Returns void +** +*******************************************************************************/ +static void smp_genenrate_ltk_cont(tSMP_CB *p_cb, tSMP_INT_DATA *p_data) +{ + BT_OCTET16 er; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + SMP_TRACE_DEBUG0 ("smp_genenrate_ltk_cont "); + BTM_GetDeviceEncRoot(er); + + /* LTK = d1(ER, DIV, 0)= e(ER, DIV)*/ + if (!SMP_Encrypt(er, BT_OCTET16_LEN, (UINT8 *)&p_cb->div, + sizeof(UINT16), &output)) + { + SMP_TRACE_ERROR0("smp_genenrate_ltk_cont failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + /* mask the LTK */ + smp_mask_enc_key(p_cb->loc_enc_size, output.param_buf); + memcpy((void *)p_cb->ltk, output.param_buf, BT_OCTET16_LEN); + smp_generate_rand_vector(p_cb, NULL); + } + +} + +/******************************************************************************* +** +** Function smp_generate_y +** +** Description This function is to proceed generate Y = E(DHK, Rand) +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_y(tSMP_CB *p_cb, tSMP_INT_DATA *p) +{ + BT_OCTET16 dhk; + tSMP_ENC output; + tSMP_STATUS status = SMP_PAIR_FAIL_UNKNOWN; + + + SMP_TRACE_DEBUG0 ("smp_generate_y "); + BTM_GetDeviceDHK(dhk); + + if (!SMP_Encrypt(dhk, BT_OCTET16_LEN, p_cb->enc_rand, + BT_OCTET8_LEN, &output)) + { + SMP_TRACE_ERROR0("smp_generate_y failed"); + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &status); + } + else + { + smp_process_ediv(p_cb, &output); + } +} +/******************************************************************************* +** +** Function smp_generate_rand_vector +** +** Description This function is called when LTK is generated, send state machine +** event to SMP. +** +** Returns void +** +*******************************************************************************/ +static void smp_generate_rand_vector (tSMP_CB *p_cb, tSMP_INT_DATA *p) +{ + /* generate EDIV and rand now */ + /* generate random vector */ + SMP_TRACE_DEBUG0 ("smp_generate_rand_vector "); + p_cb->rand_enc_proc = SMP_GEN_RAND_V; + if (!btsnd_hcic_ble_rand((void *)smp_rand_back)) + smp_rand_back(NULL); + +} +/******************************************************************************* +** +** Function smp_genenrate_smp_process_edivltk_cont +** +** Description This function is to calculate EDIV = Y xor DIV +** +** Returns void +** +*******************************************************************************/ +static void smp_process_ediv(tSMP_CB *p_cb, tSMP_ENC *p) +{ + tSMP_KEY key; + UINT8 *pp= p->param_buf; + UINT16 y; + + SMP_TRACE_DEBUG0 ("smp_process_ediv "); + STREAM_TO_UINT16(y, pp); + + /* EDIV = Y xor DIV */ + p_cb->ediv = p_cb->div ^ y; + /* send LTK ready */ + SMP_TRACE_ERROR0("LTK ready"); + key.key_type = SMP_KEY_TYPE_LTK; + key.p_data = p->param_buf; + + smp_sm_event(p_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_rand_back +** +** Description This function is to process the rand command finished, +** process the random/encrypted number for further action. +** +** Returns void +** +*******************************************************************************/ +static void smp_rand_back(tBTM_RAND_ENC *p) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *pp = p->param_buf; + UINT8 failure = SMP_PAIR_FAIL_UNKNOWN; + UINT8 state = p_cb->rand_enc_proc & ~0x80; + + SMP_TRACE_DEBUG1 ("smp_rand_back state=0x%x", state); + if (p && p->status == HCI_SUCCESS) + { + switch (state) + { + + case SMP_GEN_SRAND_MRAND: + memcpy((void *)p_cb->rand, p->param_buf, p->param_len); + smp_genenrate_rand_cont(p_cb, NULL); + break; + + case SMP_GEN_SRAND_MRAND_CONT: + memcpy((void *)&p_cb->rand[8], p->param_buf, p->param_len); + smp_genenrate_confirm(p_cb, NULL); + break; + + case SMP_GEN_DIV_LTK: + STREAM_TO_UINT16(p_cb->div, pp); + smp_genenrate_ltk_cont(p_cb, NULL); + break; + + case SMP_GEN_DIV_CSRK: + STREAM_TO_UINT16(p_cb->div, pp); + smp_compute_csrk(p_cb, NULL); + break; + + case SMP_GEN_TK: + smp_proc_passkey(p_cb, p); + break; + + case SMP_GEN_RAND_V: + memcpy(p_cb->enc_rand, p->param_buf, BT_OCTET8_LEN); + smp_generate_y(p_cb, NULL); + break; + + } + + return; + } + + SMP_TRACE_ERROR1("smp_rand_back Key generation failed: (%d)", p_cb->rand_enc_proc); + + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + +} +#endif + diff --git a/stack/smp/smp_l2c.c b/stack/smp/smp_l2c.c new file mode 100644 index 0000000..648cd59 --- /dev/null +++ b/stack/smp/smp_l2c.c @@ -0,0 +1,161 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the SMP L2Cap interface + * + ******************************************************************************/ + +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + +#include +#include "btm_ble_api.h" +#include "l2c_api.h" + +#include "smp_int.h" + + + +static void smp_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason); +static void smp_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf); + +/******************************************************************************* +** +** Function smp_l2cap_if_init +** +** Description This function is called during the SMP task startup +** to register interface functions with L2CAP. +** +*******************************************************************************/ +void smp_l2cap_if_init (void) +{ + tL2CAP_FIXED_CHNL_REG fixed_reg; + SMP_TRACE_EVENT0 ("SMDBG l2c smp_l2cap_if_init"); + fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE; + fixed_reg.fixed_chnl_opts.max_transmit = 0; + fixed_reg.fixed_chnl_opts.rtrans_tout = 0; + fixed_reg.fixed_chnl_opts.mon_tout = 0; + fixed_reg.fixed_chnl_opts.mps = 0; + fixed_reg.fixed_chnl_opts.tx_win_sz = 0; + + fixed_reg.pL2CA_FixedConn_Cb = smp_connect_cback; + fixed_reg.pL2CA_FixedData_Cb = smp_data_ind; + fixed_reg.default_idle_tout = 60; /* set 60 seconds timeout, 0xffff default idle timeout */ + + /* Now, register with L2CAP */ + L2CA_RegisterFixedChannel (L2CAP_SMP_CID, &fixed_reg); +} + +/******************************************************************************* +** +** Function smp_connect_cback +** +** Description This callback function is called by L2CAP to indicate that +** SMP channel is +** connected (conn = TRUE)/disconnected (conn = FALSE). +** +*******************************************************************************/ +static void smp_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason) +{ + tSMP_CB *p_cb = &smp_cb; + tSMP_INT_DATA int_data; + + SMP_TRACE_EVENT0 ("SMDBG l2c smp_connect_cback "); + + if (memcmp(bd_addr, p_cb->pairing_bda, BD_ADDR_LEN) == 0) + { + SMP_TRACE_EVENT3 ("smp_connect_cback() for pairing BDA: %08x%04x Event: %s", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8) + bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], (connected) ? "connected" : "disconnected"); + + if (connected) + { + if(!p_cb->connect_initialized) + { + p_cb->connect_initialized = TRUE; + /* initiating connection established */ + p_cb->role = L2CA_GetBleConnRole(bd_addr); + + /* initialize local i/r key to be default keys */ + p_cb->loc_r_key = p_cb->loc_i_key = SMP_SEC_DEFAULT_KEY; + p_cb->loc_auth_req = p_cb->peer_auth_req = SMP_DEFAULT_AUTH_REQ; + p_cb->cb_evt = SMP_IO_CAP_REQ_EVT; + smp_sm_event(p_cb, SMP_L2CAP_CONN_EVT, NULL); + + BTM_ReadConnectionAddr(p_cb->local_bda); + } + } + else + { + int_data.reason = reason; + /* Disconnected while doing security */ + smp_sm_event(p_cb, SMP_L2CAP_DISCONN_EVT, &int_data); + } + } +} + +/******************************************************************************* +** +** Function smp_data_ind +** +** Description This function is called when data is received from L2CAP on +** SMP channel. +** +** +** Returns void +** +*******************************************************************************/ +static void smp_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 cmd ; + SMP_TRACE_EVENT0 ("SMDBG l2c smp_data_ind"); + + SMP_TRACE_EVENT0 ("Got smp_data_ind"); + + STREAM_TO_UINT8(cmd, p); + + + /* reject the pairing request if there is an on-going SMP pairing */ + if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) + { + if (p_cb->state == SMP_ST_IDLE) + { + p_cb->role = L2CA_GetBleConnRole(bd_addr); + memcpy(&p_cb->pairing_bda[0], bd_addr, BD_ADDR_LEN); + } + else if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN)) + { + p_cb->failure = SMP_PAIR_NOT_SUPPORT; + smp_send_cmd(SMP_OPCODE_PAIRING_FAILED, p_cb); + } + } + + if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN) == 0) + { + btu_stop_timer (&p_cb->rsp_timer_ent); + smp_sm_event(p_cb, cmd, p); + } + + GKI_freebuf (p_buf); +} +#endif diff --git a/stack/smp/smp_main.c b/stack/smp/smp_main.c new file mode 100644 index 0000000..a60f54b --- /dev/null +++ b/stack/smp/smp_main.c @@ -0,0 +1,518 @@ +/****************************************************************************** + * + * 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. + * + ******************************************************************************/ + +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + + #include + #include "smp_int.h" + + +const char * const smp_state_name [] = +{ + "SMP_ST_IDLE", + "SMP_ST_WAIT_APP_RSP", + "SMP_ST_SEC_REQ_PENDING", + "SMP_ST_PAIR_REQ_RSP", + "SMP_ST_WAIT_CONFIRM", + "SMP_ST_CONFIRM", + "SMP_ST_RAND", + "SMP_ST_ENC_PENDING", + "SMP_ST_BOND_PENDING", + "SMP_ST_RELEASE_DELAY", + "SMP_ST_MAX" +}; +const char * const smp_event_name [] = +{ + "PAIRING_REQ_EVT", + "PAIRING_RSP_EVT", + "CONFIRM_EVT", + "RAND_EVT", + "PAIRING_FAILED_EVT", + "ENC_INFO_EVT", + "MASTER_ID_EVT", + "ID_INFO_EVT", + "ID_ADDR_EVT", + "SIGN_INFO_EVT", + "SECURITY_REQ_EVT", + "KEY_READY_EVT", + "ENCRYPTED_EVT", + "L2CAP_CONN_EVT", + "L2CAP_DISCONN_EVT", + "API_IO_RSP_EVT", + "API_SEC_GRANT_EVT", + "TK_REQ_EVT", + "AUTH_CMPL_EVT", + "ENC_REQ_EVT", + "BOND_REQ_EVT", + "DISCARD_SEC_REQ_EVT", + "RELEASE_DELAY_EVT", + "RELEASE_DELAY_TOUT_EVT", + "MAX_EVT" +}; +const char * smp_get_event_name(tSMP_EVENT event); +const char * smp_get_state_name(tSMP_STATE state); + + #define SMP_SM_IGNORE 0 + #define SMP_NUM_ACTIONS 2 + #define SMP_SME_NEXT_STATE 2 + #define SMP_SM_NUM_COLS 3 +typedef const UINT8 (*tSMP_SM_TBL)[SMP_SM_NUM_COLS]; + +enum +{ + SMP_PROC_SEC_REQ, + SMP_SEND_PAIR_REQ, + SMP_SEND_PAIR_RSP, + SMP_SEND_CONFIRM, + SMP_SEND_PAIR_FAIL, + SMP_SEND_INIT, + SMP_SEND_SECU_INFO, + SMP_SEND_ID_INFO, + SMP_SEND_LTK_REPLY, + SMP_PROC_PAIR_CMD, + SMP_PROC_PAIR_FAIL, + SMP_PROC_CONFIRM, + SMP_PROC_INIT, + SMP_PROC_ENC_INFO, + SMP_PROC_MASTER_ID, + SMP_PROC_ID_INFO, + SMP_PROC_ID_ADDR, + SMP_PROC_SRK_INFO, + SMP_PROC_SEC_GRANT, + SMP_PROC_SL_KEY, + SMP_PROC_COMPARE, + SMP_PROC_IO_RSP, + SMP_GENERATE_COMPARE, + SMP_GENERATE_CONFIRM, + SMP_GENERATE_STK, + SMP_KEY_DISTRIBUTE, + SMP_START_ENC, + SMP_PAIRING_CMPL, + SMP_DECIDE_ASSO_MODEL, + SMP_SEND_APP_CBACK, + SMP_CHECK_AUTH_REQ, + SMP_PAIR_TERMINATE, + SMP_ENC_CMPL, + SMP_PROC_DISCARD, + SMP_PROC_REL_DELAY, + SMP_PROC_REL_DELAY_TOUT, + SMP_SM_NO_ACTION +}; + +static const tSMP_ACT smp_sm_action[] = +{ + smp_proc_sec_req, + smp_send_pair_req, + smp_send_pair_rsp, + smp_send_confirm, + smp_send_pair_fail, + smp_send_init, + smp_send_enc_info, + smp_send_id_info, + smp_send_ltk_reply, + smp_proc_pair_cmd, + smp_proc_pair_fail, + smp_proc_confirm, + smp_proc_init, + smp_proc_enc_info, + smp_proc_master_id, + smp_proc_id_info, + smp_proc_id_addr, + smp_proc_srk_info, + smp_proc_sec_grant, + smp_proc_sl_key, + smp_proc_compare, + smp_proc_io_rsp, + smp_generate_compare, + smp_generate_confirm, + smp_generate_stk, + smp_key_distribution, + smp_start_enc, + smp_pairing_cmpl, + smp_decide_asso_model, + smp_send_app_cback, + smp_check_auth_req, + smp_pair_terminate, + smp_enc_cmpl, + smp_proc_discard, + smp_proc_release_delay, + smp_proc_release_delay_tout, +}; +/************ SMP Master FSM State/Event Indirection Table **************/ +static const UINT8 smp_ma_entry_map[][SMP_ST_MAX] = +{ +/* state name: Idle WaitApp SecReq Pair Wait Confirm Init Enc Bond Rel + Rsp Pend ReqRsp Cfm Pend Pend Delay */ +/* PAIR_REQ */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* PAIR_RSP */{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, +/* CONFIRM */{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, +/* INIT */{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, +/* PAIR_FAIL */{ 0, 0x81, 0, 0x81, 0x81,0x81, 0x81,0, 0, 0 }, +/* ENC_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, +/* MASTER_ID */{ 0, 0, 0, 0, 0, 0, 0, 0, 4, 0 }, +/* ID_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }, +/* ID_ADDR */{ 0, 0, 0, 0, 0, 0, 0, 0, 5, 0 }, +/* SIGN_INFO */{ 0, 0, 0, 0, 0, 0, 0, 0, 3, 0 }, +/* SEC_REQ */{ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* KEY_READY */{ 0, 3, 0, 3, 1, 0, 2, 1, 6, 0 }, +/* ENC_CMPL */{ 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 }, +/* L2C_CONN */{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* L2C_DISC */{ 0, 0x83, 0, 0x83, 0x83,0x83, 0x83,0x83, 0x83, 2 }, +/* IO_RSP */{ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* SEC_GRANT */{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* TK_REQ */{ 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, +/* AUTH_CMPL */{ 0, 0x82, 0, 0x82, 0x82,0x82, 0x82,0x82, 0x82, 0 }, +/* ENC_REQ */{ 0, 4, 0, 0, 0, 0, 3, 0, 0, 0 }, +/* BOND_REQ */{ 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, +/* DISCARD_SEC_REQ */{ 0, 5, 0, 0, 0, 0, 0, 3, 0, 0 }, +/* RELEASE_DELAY */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, +/* RELEASE_DELAY_TOUT */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }, +}; + +static const UINT8 smp_all_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PAIR_FAIL */ {SMP_PROC_PAIR_FAIL, SMP_PROC_REL_DELAY_TOUT, SMP_ST_IDLE}, +/* AUTH_CMPL */ {SMP_SEND_PAIR_FAIL, SMP_PAIRING_CMPL, SMP_ST_RELEASE_DELAY}, +/* L2C_DISC */ {SMP_PAIR_TERMINATE, SMP_SM_NO_ACTION, SMP_ST_IDLE} +}; + +static const UINT8 smp_ma_idle_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* L2C_CONN */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_ST_WAIT_APP_RSP}, +/* SEC_REQ */ {SMP_PROC_SEC_REQ, SMP_SEND_APP_CBACK, SMP_ST_WAIT_APP_RSP} +}; + +static const UINT8 smp_ma_wait_app_rsp_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* SEC_GRANT */ { SMP_PROC_SEC_GRANT, SMP_SEND_APP_CBACK, SMP_ST_WAIT_APP_RSP}, +/* IO_RSP */ { SMP_SEND_PAIR_REQ, SMP_SM_NO_ACTION, SMP_ST_PAIR_REQ_RSP}, +/* KEY_READY */ { SMP_GENERATE_CONFIRM, SMP_SM_NO_ACTION, SMP_ST_WAIT_CONFIRM},/* TK ready */ +/* ENC_REQ */ { SMP_START_ENC, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING},/* start enc mode setup */ +/* DISCARD_SEC_REQ */ { SMP_PROC_DISCARD, SMP_SM_NO_ACTION, SMP_ST_IDLE} +}; + +static const UINT8 smp_ma_pair_req_rsp_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PAIR_RSP */ { SMP_PROC_PAIR_CMD, SMP_DECIDE_ASSO_MODEL, SMP_ST_PAIR_REQ_RSP}, +/* TK_REQ */ { SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_ST_WAIT_APP_RSP}, +/* KEY_READY */{ SMP_GENERATE_CONFIRM, SMP_SM_NO_ACTION, SMP_ST_WAIT_CONFIRM} /* TK ready */ +}; + +static const UINT8 smp_ma_wait_confirm_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* KEY_READY*/ {SMP_SEND_CONFIRM, SMP_SM_NO_ACTION, SMP_ST_CONFIRM}/* CONFIRM ready */ +}; + +static const UINT8 smp_ma_confirm_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONFIRM */ { SMP_PROC_CONFIRM, SMP_SEND_INIT, SMP_ST_RAND} +}; + +static const UINT8 smp_ma_init_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* INIT */ { SMP_PROC_INIT, SMP_GENERATE_COMPARE, SMP_ST_RAND}, +/* KEY_READY*/ { SMP_PROC_COMPARE, SMP_SM_NO_ACTION, SMP_ST_RAND}, /* Compare ready */ +/* ENC_REQ */ { SMP_GENERATE_STK, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING} +}; +static const UINT8 smp_ma_enc_pending_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* KEY_READY */ { SMP_START_ENC, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING}, /* STK ready */ +/* ENCRYPTED */ { SMP_CHECK_AUTH_REQ, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING}, +/* BOND_REQ */ { SMP_KEY_DISTRIBUTE, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING} +}; +static const UINT8 smp_ma_bond_pending_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* ENC_INFO */ { SMP_PROC_ENC_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* ID_INFO */ { SMP_PROC_ID_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* SIGN_INFO*/ { SMP_PROC_SRK_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* MASTER_ID*/ { SMP_PROC_MASTER_ID, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* ID_ADDR */ { SMP_PROC_ID_ADDR, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* KEY_READY */ {SMP_SEND_SECU_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING} /* LTK ready */ +}; + +static const UINT8 smp_ma_rel_delay_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* RELEASE_DELAY*/ {SMP_PROC_REL_DELAY, SMP_SM_NO_ACTION, SMP_ST_RELEASE_DELAY}, +/* RELEASE_DELAY_TOUT*/ {SMP_PROC_REL_DELAY_TOUT, SMP_SM_NO_ACTION, SMP_ST_IDLE} +}; + + +/************ SMP Slave FSM State/Event Indirection Table **************/ +static const UINT8 smp_sl_entry_map[][SMP_ST_MAX] = +{ +/* state name: Idle Wait SecReq Pair Wait Confirm Init Enc Bond Rel + AppRsp Pend ReqRsp Cfm Pend Pend Delay */ +/* PAIR_REQ */ { 2, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, +/* PAIR_RSP */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* CONFIRM */ { 0, 4, 0, 1, 1, 0, 0, 0, 0, 0 }, +/* INIT */ { 0, 0, 0, 0, 0, 1, 2, 0, 0, 0 }, +/* PAIR_FAIL*/ { 0, 0x81, 0x81, 0x81, 0x81,0x81, 0x81,0x81, 0, 0 }, +/* ENC_INFO */ { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0 }, +/* MASTER_ID*/ { 0, 0, 0, 0, 0, 0, 0, 0, 5, 0 }, +/* ID_INFO */ { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0 }, +/* ID_ADDR */ { 0, 0, 0, 0, 0, 0, 0, 0, 6, 0 }, +/* SIGN_INFO*/ { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0 }, +/* SEC_REQ */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + +/* KEY_READY*/ { 0, 3, 0, 3, 2, 2, 1, 2, 1, 0 }, +/* ENC_CMPL */ { 0, 0, 2, 0, 0, 0, 0, 3, 0, 0 }, +/* L2C_CONN */ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* L2C_DISC */ { 0, 0x83, 0x83, 0x83, 0x83,0x83, 0x83,0x83, 0x83, 2 }, +/* IO_RSP */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* SEC_GRANT*/ { 0, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + +/* TK_REQ */ { 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, +/* AUTH_CMPL*/ { 0, 0x82, 0x82, 0x82, 0x82,0x82, 0x82,0x82, 0x82, 0 }, +/* ENC_REQ */ { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, +/* BOND_REQ */ { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0 }, +/* DISCARD_SEC_REQ */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* RELEASE_DELAY */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, +/* RELEASE_DELAY_TOUT */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }, +}; + +static const UINT8 smp_sl_idle_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* L2C_CONN */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_ST_WAIT_APP_RSP}, +/* PAIR_REQ */ {SMP_PROC_PAIR_CMD, SMP_SEND_APP_CBACK, SMP_ST_WAIT_APP_RSP} +}; + +static const UINT8 smp_sl_wait_app_rsp_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* IO_RSP */ {SMP_PROC_IO_RSP, SMP_SM_NO_ACTION, SMP_ST_PAIR_REQ_RSP}, +/* SEC_GRANT */ {SMP_PROC_SEC_GRANT, SMP_SEND_APP_CBACK, SMP_ST_WAIT_APP_RSP}, +/* KEY_READY */ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_ST_WAIT_APP_RSP},/* TK ready */ +/* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SM_NO_ACTION, SMP_ST_CONFIRM} +}; + +static const UINT8 smp_sl_sec_request_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PAIR_REQ */{SMP_PROC_PAIR_CMD, SMP_SEND_PAIR_RSP, SMP_ST_PAIR_REQ_RSP}, +/* ENCRYPTED*/{SMP_ENC_CMPL, SMP_SM_NO_ACTION, SMP_ST_PAIR_REQ_RSP}, +}; + +static const UINT8 smp_sl_pair_req_rsp_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SM_NO_ACTION, SMP_ST_CONFIRM}, +/* TK_REQ */ {SMP_SEND_APP_CBACK, SMP_SM_NO_ACTION, SMP_ST_WAIT_APP_RSP}, +/* KEY_READY */{SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_ST_PAIR_REQ_RSP} /* TK/Confirm ready */ + +}; + +static const UINT8 smp_sl_wait_confirm_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONFIRM */ {SMP_PROC_CONFIRM, SMP_SEND_CONFIRM, SMP_ST_CONFIRM}, +/* KEY_READY*/ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_ST_WAIT_CONFIRM} +}; +static const UINT8 smp_sl_confirm_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* INIT_EVT */{ SMP_PROC_INIT, SMP_GENERATE_COMPARE, SMP_ST_RAND}, +/* KEY_READY*/ {SMP_PROC_SL_KEY, SMP_SM_NO_ACTION, SMP_ST_CONFIRM} /* TK/Confirm ready */ +}; +static const UINT8 smp_sl_init_table [][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* KEY_READY */ {SMP_PROC_COMPARE, SMP_SM_NO_ACTION, SMP_ST_RAND}, /* compare match */ +/* INIT_EVT */ {SMP_SEND_INIT, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING} +}; +static const UINT8 smp_sl_enc_pending_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* ENC_REQ */ {SMP_GENERATE_STK, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING}, +/* KEY_READY */ {SMP_SEND_LTK_REPLY, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING},/* STK ready */ +/* ENCRYPTED */ {SMP_CHECK_AUTH_REQ, SMP_SM_NO_ACTION, SMP_ST_ENC_PENDING}, +/* BOND_REQ */ {SMP_KEY_DISTRIBUTE, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING} +}; +static const UINT8 smp_sl_bond_pending_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* KEY_READY */ {SMP_SEND_SECU_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, /* LTK ready */ +/* SIGN_INFO */ {SMP_PROC_SRK_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, /* rev SRK */ +/* ENC_INFO */ { SMP_PROC_ENC_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* ID_INFO */ { SMP_PROC_ID_INFO, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* MASTER_ID*/ { SMP_PROC_MASTER_ID, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING}, +/* ID_ADDR */ { SMP_PROC_ID_ADDR, SMP_SM_NO_ACTION, SMP_ST_BOND_PENDING} + +}; + +static const UINT8 smp_sl_rel_delay_table[][SMP_SM_NUM_COLS] = { +/* Event Action Next State */ +/* RELEASE_DELAY*/ {SMP_PROC_REL_DELAY, SMP_SM_NO_ACTION, SMP_ST_RELEASE_DELAY}, +/* RELEASE_DELAY_TOUT*/ {SMP_PROC_REL_DELAY_TOUT, SMP_SM_NO_ACTION, SMP_ST_IDLE} +}; + +static const tSMP_SM_TBL smp_state_table[][2] = { + {smp_ma_idle_table, smp_sl_idle_table}, /* SMP_ST_IDLE*/ + {smp_ma_wait_app_rsp_table, smp_sl_wait_app_rsp_table}, /* SMP_ST_WAIT_APP_RSP */ + {NULL, smp_sl_sec_request_table}, /* SMP_ST_SEC_REQ_PENDING */ + {smp_ma_pair_req_rsp_table, smp_sl_pair_req_rsp_table}, /* SMP_ST_PAIR_REQ_RSP */ + {smp_ma_wait_confirm_table, smp_sl_wait_confirm_table}, /* SMP_ST_WAIT_CONFIRM */ + {smp_ma_confirm_table, smp_sl_confirm_table}, /* SMP_ST_CONFIRM */ + {smp_ma_init_table, smp_sl_init_table}, /* SMP_ST_RAND */ + {smp_ma_enc_pending_table, smp_sl_enc_pending_table}, /* SMP_ST_ENC_PENDING */ + {smp_ma_bond_pending_table, smp_sl_bond_pending_table}, /* SMP_ST_BOND_PENDING */ + {smp_ma_rel_delay_table, smp_sl_rel_delay_table} /* SMP_ST_RELEASE_DELAY */ +}; + +typedef const UINT8 (*tSMP_ENTRY_TBL)[SMP_ST_MAX]; +static const tSMP_ENTRY_TBL smp_entry_table[] ={ + smp_ma_entry_map, + smp_sl_entry_map +}; + +#if SMP_DYNAMIC_MEMORY == FALSE +tSMP_CB smp_cb; +#endif +#define SMP_ALL_TBL_MASK 0x80 + + +/******************************************************************************* +** Function smp_set_state +** Returns None +*******************************************************************************/ +void smp_set_state(tSMP_STATE state) +{ + if (state < SMP_ST_MAX) + { + SMP_TRACE_DEBUG4( "State change: %s(%d) ==> %s(%d)", + smp_get_state_name(smp_cb.state), smp_cb.state, + smp_get_state_name(state), state ); + smp_cb.state = state; + } + else + { + SMP_TRACE_DEBUG1("smp_set_state invalid state =%d", state ); + } +} + +/******************************************************************************* +** Function smp_get_state +** Returns The smp state +*******************************************************************************/ +tSMP_STATE smp_get_state(void) +{ + return smp_cb.state; +} + + +/******************************************************************************* +** +** Function smp_sm_event +** +** Description Handle events to the state machine. It looks up the entry +** in the smp_entry_table array. +** If it is a valid entry, it gets the state table.Set the next state, +** if not NULL state.Execute the action function according to the +** state table. If the state returned by action function is not NULL +** state, adjust the new state to the returned state.If (api_evt != MAX), +** call callback function. +** +** Returns void. +** +*******************************************************************************/ +void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data) +{ + UINT8 curr_state = p_cb->state; + tSMP_SM_TBL state_table; + UINT8 action, entry, i; + tSMP_ENTRY_TBL entry_table = smp_entry_table[p_cb->role]; + + SMP_TRACE_EVENT0("main smp_sm_event"); + if (curr_state >= SMP_ST_MAX) + { + SMP_TRACE_DEBUG1( "Invalid state: %d", curr_state) ; + return; + } + + SMP_TRACE_DEBUG5( "SMP Role: %s State: [%s (%d)], Event: [%s (%d)]",\ + (p_cb->role == 0x01) ?"Slave" : "Master", smp_get_state_name( p_cb->state), + p_cb->state, smp_get_event_name(event), event) ; + + /* look up the state table for the current state */ + /* lookup entry /w event & curr_state */ + /* If entry is ignore, return. + * Otherwise, get state table (according to curr_state or all_state) */ + if ( (entry = entry_table[event - 1][curr_state]) != SMP_SM_IGNORE ) + { + if (entry & SMP_ALL_TBL_MASK) + { + entry &= ~SMP_ALL_TBL_MASK; + state_table = smp_all_table; + } + else + state_table = smp_state_table[curr_state][p_cb->role]; + } + else + { + SMP_TRACE_DEBUG4( "Ignore event [%s (%d)] in state [%s (%d)]", + smp_get_event_name(event), event, smp_get_state_name(curr_state), curr_state); + return; + } + + /* Get possible next state from state table. */ + + smp_set_state(state_table[entry-1][SMP_SME_NEXT_STATE]); + + /* If action is not ignore, clear param, exec action and get next state. + * The action function may set the Param for cback. + * Depending on param, call cback or free buffer. */ + /* execute action */ + /* execute action functions */ + for (i = 0; i < SMP_NUM_ACTIONS; i++) + { + if ((action = state_table[entry-1][i]) != SMP_SM_NO_ACTION) + { + (*smp_sm_action[action])(p_cb, (tSMP_INT_DATA *)p_data); + } + else + { + break; + } + } + SMP_TRACE_DEBUG1( "result state = %s", smp_get_state_name( p_cb->state ) ) ; +} + + +/******************************************************************************* +** Function smp_get_state_name +** Returns The smp state name. +*******************************************************************************/ +const char * smp_get_state_name(tSMP_STATE state) +{ + const char * p_str = smp_state_name[SMP_ST_MAX]; + + if (state < SMP_ST_MAX) + { + p_str = smp_state_name[state]; + } + return p_str; +} +/******************************************************************************* +** Function smp_get_event_name +** Returns The smp event name. +*******************************************************************************/ +const char * smp_get_event_name(tSMP_EVENT event) +{ + const char * p_str = smp_event_name[SMP_MAX_EVT - 1]; + + if (event < SMP_MAX_EVT) + { + p_str = smp_event_name[event- 1]; + } + return p_str; +} +#endif + diff --git a/stack/smp/smp_utils.c b/stack/smp/smp_utils.c new file mode 100644 index 0000000..5622880 --- /dev/null +++ b/stack/smp/smp_utils.c @@ -0,0 +1,675 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the SMP L2Cap utility functions + * + ******************************************************************************/ +#include "bt_target.h" + +#if SMP_INCLUDED == TRUE + +#include "bt_types.h" +#include +#include +#include "hcidefs.h" +#include "btm_ble_api.h" +#include "l2c_api.h" +#include "l2c_int.h" +#include "smp_int.h" + + +#define SMP_PAIRING_REQ_SIZE 7 +#define SMP_CONFIRM_CMD_SIZE (BT_OCTET16_LEN + 1) +#define SMP_INIT_CMD_SIZE (BT_OCTET16_LEN + 1) +#define SMP_ENC_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_MASTER_ID_SIZE (BT_OCTET8_LEN + 2 + 1) +#define SMP_ID_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_ID_ADDR_SIZE (BD_ADDR_LEN + 1 + 1) +#define SMP_SIGN_INFO_SIZE (BT_OCTET16_LEN + 1) +#define SMP_PAIR_FAIL_SIZE 2 + + +/* type for action functions */ +typedef BT_HDR * (*tSMP_CMD_ACT)(UINT8 cmd_code, tSMP_CB *p_cb); + +static BT_HDR * smp_build_pairing_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_confirm_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_rand_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_pairing_fail(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_encrypt_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_security_request(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_signing_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_master_id_cmd(UINT8 cmd_code, tSMP_CB *p_cb); +static BT_HDR * smp_build_id_addr_cmd(UINT8 cmd_code, tSMP_CB *p_cb); + +const tSMP_CMD_ACT smp_cmd_build_act[] = +{ + NULL, + smp_build_pairing_cmd, /* 0x01: pairing request */ + smp_build_pairing_cmd, /* 0x02: pairing response */ + smp_build_confirm_cmd, /* 0x03: pairing confirm */ + smp_build_rand_cmd, /* 0x04: pairing initializer request */ + smp_build_pairing_fail, /* 0x05: pairing failure */ + smp_build_encrypt_info_cmd, /* 0x06: security information command */ + smp_build_master_id_cmd, /* 0x07: master identity command */ + smp_build_identity_info_cmd, /* 0x08: identity information command */ + smp_build_id_addr_cmd, /* 0x09: signing information */ + smp_build_signing_info_cmd, /* 0x0A: signing information */ + smp_build_security_request /* 0x0B: security request */ +}; +/******************************************************************************* +** +** Function smp_send_msg_to_L2CAP +** +** Description Send message to L2CAP. +** +*******************************************************************************/ +BOOLEAN smp_send_msg_to_L2CAP(BD_ADDR rem_bda, BT_HDR *p_toL2CAP) +{ + UINT16 l2cap_ret; + + SMP_TRACE_EVENT0("smp_send_msg_to_L2CAP"); + + if ((l2cap_ret = L2CA_SendFixedChnlData (L2CAP_SMP_CID, rem_bda, p_toL2CAP)) == L2CAP_DW_FAILED) + { + SMP_TRACE_ERROR1("SMP failed to pass msg:0x%0x to L2CAP", + *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); + GKI_freebuf(p_toL2CAP); + return FALSE; + } + else + { + return TRUE; + } +} +/******************************************************************************* +** +** Function smp_send_cmd +** +** Description send a SMP command on L2CAP channel. +** +*******************************************************************************/ +BOOLEAN smp_send_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf; + BOOLEAN sent = FALSE; + UINT8 failure = SMP_PAIR_INTERNAL_ERR; + SMP_TRACE_EVENT1("smp_send_cmd on l2cap cmd_code=0x%x", cmd_code); + if ( cmd_code < SMP_OPCODE_MAX && + smp_cmd_build_act[cmd_code] != NULL) + { + p_buf = (*smp_cmd_build_act[cmd_code])(cmd_code, p_cb); + + if (p_buf != NULL && + smp_send_msg_to_L2CAP(p_cb->pairing_bda, p_buf)) + { + sent = TRUE; + + btu_stop_timer (&p_cb->rsp_timer_ent); + btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, + SMP_WAIT_FOR_RSP_TOUT); + } + } + + if (!sent) + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } + return sent; +} + + + +/******************************************************************************* +** +** Function smp_rsp_timeout +** +** Description Called when SMP wait for SMP command response timer expires +** +** Returns void +** +*******************************************************************************/ +void smp_rsp_timeout(TIMER_LIST_ENT *p_tle) +{ + tSMP_CB *p_cb = &smp_cb; + UINT8 failure = SMP_RSP_TIMEOUT; + + SMP_TRACE_EVENT1("smp_rsp_timeout state:%d", p_cb->state); + + if (smp_get_state() == SMP_ST_RELEASE_DELAY) + { + smp_sm_event(p_cb, SMP_RELEASE_DELAY_TOUT_EVT, NULL); + } + else + { + smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &failure); + } +} + +/******************************************************************************* +** +** Function smp_build_pairing_req_cmd +** +** Description Build pairing request command. +** +*******************************************************************************/ +BT_HDR * smp_build_pairing_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_pairing_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_PAIRING_REQ_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, cmd_code); + UINT8_TO_STREAM (p, p_cb->loc_io_caps); + UINT8_TO_STREAM (p, p_cb->loc_oob_flag); + UINT8_TO_STREAM (p, p_cb->loc_auth_req); + UINT8_TO_STREAM (p, p_cb->loc_enc_size); + UINT8_TO_STREAM (p, p_cb->loc_i_key); + UINT8_TO_STREAM (p, p_cb->loc_r_key); + + p_buf->offset = L2CAP_MIN_OFFSET; + /* 1B ERR_RSP op code + 1B cmd_op_code + 2B handle + 1B status */ + p_buf->len = SMP_PAIRING_REQ_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_confirm_cmd +** +** Description Build confirm request command. +** +*******************************************************************************/ +static BT_HDR * smp_build_confirm_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_confirm_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_CONFIRM_CMD_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_CONFIRM); + ARRAY_TO_STREAM (p, p_cb->confirm, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_CONFIRM_CMD_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_rand_cmd +** +** Description Build Initializer command. +** +*******************************************************************************/ +static BT_HDR * smp_build_rand_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_rand_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_INIT_CMD_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_INIT); + ARRAY_TO_STREAM (p, p_cb->rand, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_INIT_CMD_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_encrypt_info_cmd +** +** Description Build security information command. +** +*******************************************************************************/ +static BT_HDR * smp_build_encrypt_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_encrypt_info_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_ENC_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_ENCRYPT_INFO); + ARRAY_TO_STREAM (p, p_cb->ltk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ENC_INFO_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_master_id_cmd +** +** Description Build security information command. +** +*******************************************************************************/ +static BT_HDR * smp_build_master_id_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_master_id_cmd "); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_MASTER_ID_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_MASTER_ID); + UINT16_TO_STREAM (p, p_cb->ediv); + ARRAY_TO_STREAM (p, p_cb->enc_rand, BT_OCTET8_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_MASTER_ID_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_identity_info_cmd +** +** Description Build identity information command. +** +*******************************************************************************/ +static BT_HDR * smp_build_identity_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + BT_OCTET16 irk; + SMP_TRACE_EVENT0("smp_build_identity_info_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_ID_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + BTM_GetDeviceIDRoot(irk); + + UINT8_TO_STREAM (p, SMP_OPCODE_IDENTITY_INFO); + ARRAY_TO_STREAM (p, irk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ID_INFO_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_id_addr_cmd +** +** Description Build identity address information command. +** +*******************************************************************************/ +static BT_HDR * smp_build_id_addr_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + BT_OCTET16 irk; + SMP_TRACE_EVENT0("smp_build_id_addr_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_ID_ADDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + BTM_GetDeviceIDRoot(irk); + + UINT8_TO_STREAM (p, SMP_OPCODE_ID_ADDR); + UINT8_TO_STREAM (p, 0); /* TODO: update with local address type */ + BTM_ReadConnectionAddr(p_cb->local_bda); + BDADDR_TO_STREAM (p, p_cb->local_bda); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_ID_ADDR_SIZE; + } + + return p_buf; +} + +/******************************************************************************* +** +** Function smp_build_signing_info_cmd +** +** Description Build signing information command. +** +*******************************************************************************/ +static BT_HDR * smp_build_signing_info_cmd(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + //BT_OCTET16 srk; remove + SMP_TRACE_EVENT0("smp_build_signing_info_cmd"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_SIGN_INFO_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_SIGN_INFO); + ARRAY_TO_STREAM (p, p_cb->csrk, BT_OCTET16_LEN); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_SIGN_INFO_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_pairing_fail +** +** Description Build Pairing Fail command. +** +*******************************************************************************/ +static BT_HDR * smp_build_pairing_fail(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_pairing_fail"); + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + SMP_PAIR_FAIL_SIZE + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_PAIRING_FAILED); + UINT8_TO_STREAM (p, p_cb->failure); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = SMP_PAIR_FAIL_SIZE; + } + + return p_buf; +} +/******************************************************************************* +** +** Function smp_build_security_request +** +** Description Build security request command. +** +*******************************************************************************/ +static BT_HDR * smp_build_security_request(UINT8 cmd_code, tSMP_CB *p_cb) +{ + BT_HDR *p_buf = NULL ; + UINT8 *p; + SMP_TRACE_EVENT0("smp_build_security_request"); + + if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 2 + L2CAP_MIN_OFFSET)) != NULL) + { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, SMP_OPCODE_SEC_REQ); + UINT8_TO_STREAM (p, p_cb->loc_auth_req); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 2; + + SMP_TRACE_EVENT2("opcode=%d auth_req=0x%x",SMP_OPCODE_SEC_REQ, p_cb->loc_auth_req ); + } + + return p_buf; + +} + +/******************************************************************************* +** +** Function smp_convert_string_to_tk +** +** Description This function is called to convert a 6 to 16 digits numeric +** character string into SMP TK. +** +** +** Returns void +** +*******************************************************************************/ +void smp_convert_string_to_tk(BT_OCTET16 tk, UINT32 passkey) +{ + UINT8 *p = tk; + tSMP_KEY key; + SMP_TRACE_EVENT0("smp_convert_string_to_tk"); + UINT32_TO_STREAM(p, passkey); + + key.key_type = SMP_KEY_TYPE_TK; + key.p_data = tk; + + smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &key); +} + +/******************************************************************************* +** +** Function smp_mask_enc_key +** +** Description This function is called to mask off the encryption key based +** on the maximum encryption key size. +** +** +** Returns void +** +*******************************************************************************/ +void smp_mask_enc_key(UINT8 loc_enc_size, UINT8 * p_data) +{ + SMP_TRACE_EVENT0("smp_mask_enc_key"); + if (loc_enc_size < BT_OCTET16_LEN) + { + for (; loc_enc_size < BT_OCTET16_LEN; loc_enc_size ++) + * (p_data + loc_enc_size) = 0; + } + return; +} +/******************************************************************************* +** +** Function smp_xor_128 +** +** Description utility function to do an biteise exclusive-OR of two bit +** strings of the length of BT_OCTET16_LEN. +** +** Returns void +** +*******************************************************************************/ +void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b) +{ + UINT8 i, *aa = a, *bb = b; + + SMP_TRACE_EVENT0("smp_xor_128"); + for (i = 0; i < BT_OCTET16_LEN; i++) + { + aa[i] = aa[i] ^ bb[i]; + } +} + + +/******************************************************************************* +** +** Function smp_cb_cleanup +** +** Description Clean up SMP control block +** +** Returns void +** +*******************************************************************************/ +void smp_cb_cleanup(tSMP_CB *p_cb) +{ + tSMP_CALLBACK *p_callback = p_cb->p_callback; + UINT8 trace_level = p_cb->trace_level; + + SMP_TRACE_EVENT0("smp_cb_cleanup"); + memset(p_cb, 0, sizeof(tSMP_CB)); + p_cb->p_callback = p_callback; + p_cb->trace_level = trace_level; +} +/******************************************************************************* +** +** Function smp_reset_control_value +** +** Description This function is called to reset the control block value when +** pairing procedure finished. +** +** +** Returns void +** +*******************************************************************************/ +void smp_reset_control_value(tSMP_CB *p_cb) +{ + SMP_TRACE_EVENT0("smp_reset_control_value"); + btu_stop_timer (&p_cb->rsp_timer_ent); +#if SMP_CONFORMANCE_TESTING == TRUE + + SMP_TRACE_EVENT1("smp_cb.remove_fixed_channel_disable=%d", smp_cb.remove_fixed_channel_disable); + if (!smp_cb.remove_fixed_channel_disable) + { + L2CA_RemoveFixedChnl (L2CAP_SMP_CID, p_cb->pairing_bda); + } + else + { + SMP_TRACE_EVENT0("disable the removal of the fixed channel"); + } + + +#else + /* We can tell L2CAP to remove the fixed channel (if it has one) */ + L2CA_RemoveFixedChnl (L2CAP_SMP_CID, p_cb->pairing_bda); + +#endif + smp_cb_cleanup(p_cb); + +} +/******************************************************************************* +** +** Function smp_proc_pairing_cmpl +** +** Description This function is called to process pairing complete +** +** +** Returns void +** +*******************************************************************************/ +void smp_proc_pairing_cmpl(tSMP_CB *p_cb) +{ + tSMP_EVT_DATA evt_data = {0}; + + SMP_TRACE_DEBUG0 ("smp_proc_pairing_cmpl "); + + evt_data.cmplt.reason = p_cb->status; + + if (p_cb->status == SMP_SUCCESS) + evt_data.cmplt.sec_level = p_cb->sec_level; + + evt_data.cmplt.is_pair_cancel = FALSE; + + if (p_cb->is_pair_cancel) + evt_data.cmplt.is_pair_cancel = TRUE; + + + SMP_TRACE_DEBUG2 ("send SMP_COMPLT_EVT reason=0x%0x sec_level=0x%0x", + evt_data.cmplt.reason, + evt_data.cmplt.sec_level ); + if (p_cb->p_callback) + (*p_cb->p_callback) (SMP_COMPLT_EVT, p_cb->pairing_bda, &evt_data); + +#if 0 /* TESTING CODE : as a master, reencrypt using LTK */ + if (evt_data.cmplt.reason == 0 && p_cb->role == HCI_ROLE_MASTER) + { + btm_ble_start_encrypt(p_cb->pairing_bda, FALSE, NULL); + } +#endif + + smp_reset_control_value(p_cb); +} + +#if SMP_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function smp_set_test_confirm_value +** +** Description This function is called to set the test confirm value +** +** Returns void +** +*******************************************************************************/ +void smp_set_test_confirm_value(BOOLEAN enable, UINT8 *p_c_val) +{ + SMP_TRACE_DEBUG1("smp_set_test_confirm_value enable=%d", enable); + smp_cb.enable_test_confirm_val = enable; + memcpy(smp_cb.test_confirm, p_c_val, BT_OCTET16_LEN); +} + + +/******************************************************************************* +** +** Function smp_set_test_confirm_value +** +** Description This function is called to set the test rand value +** +** Returns void +** +*******************************************************************************/ +void smp_set_test_rand_value(BOOLEAN enable, UINT8 *p_c_val) +{ + SMP_TRACE_DEBUG1("smp_set_test_rand_value enable=%d", enable); + smp_cb.enable_test_rand_val = enable; + memcpy(smp_cb.test_rand, p_c_val, BT_OCTET16_LEN); +} + + +/******************************************************************************* +** +** Function smp_set_test_pair_fail_status +** +** Description This function is called to set the test fairing fair status +** +** Returns void +** +*******************************************************************************/ +void smp_set_test_pair_fail_status (BOOLEAN enable, UINT8 status) +{ + SMP_TRACE_DEBUG1("smp_set_test_confirm_value enable=%d", enable); + smp_cb.enable_test_pair_fail = enable; + smp_cb.pair_fail_status = status; +} + +/******************************************************************************* +** +** Function smp_set_test_pair_fail_status +** +** Description This function is called to disable the removal of fixed channel +** in smp_reset_control_value +** Returns void +** +*******************************************************************************/ +void smp_remove_fixed_channel_disable (BOOLEAN disable) +{ + SMP_TRACE_DEBUG1("smp_remove_fixed_channel_disable disable =%d", disable); + smp_cb.remove_fixed_channel_disable = disable; +} + +#endif + + +#endif + -- cgit v1.1